From e84354666ac141959104f50be3fb22f1932dbaf4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 26 Dec 2020 14:28:56 -0800 Subject: [PATCH 001/552] 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 11bf628b42..4b8755ab48 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 73c4c627df..a5f262f012 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 002/552] minor (#16478) --- compiler/llstream.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 6df927c60b..bd335c23d8 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 003/552] 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 ab45f78dc7..18b09e4c03 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 0000000000..cff37a3b77 --- /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 004/552] 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 8865558fe5..e2ceedc2c7 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 005/552] 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 0000000000..cfca0bf3f3 --- /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 0000000000..765831ba75 --- /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 0000000000..5fa9e51426 --- /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 0000000000..23f9892cc0 --- /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 0000000000..3e85ee5404 --- /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 0000000000..d6f25571b3 --- /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 2b60ed0945..c28b88b9b3 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 0000000000..299b83a3da --- /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 15fc1cd1c1..30dd60591b 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 762534310f..e1ff96553a 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 0fda43911c..23aa2d7271 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 9462c3d99e..0000000000 --- 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 1607f52448..ba2034bd3c 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 6d9817ed2f..d7ed63ed04 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 9c36ffd0f1..2f5e278c46 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 1402b762ae..1c06397c5a 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 006/552] 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 85ef15e08f..fb008dc452 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 e2ceedc2c7..5f18f01cb0 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 0000000000..2f7eda9f7a --- /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 ff3d41b492..4d5a15940e 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 007/552] 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 90330e4f45..34c9634108 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 008/552] 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 c41aee96f6..019b735f58 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 009/552] 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 3e5e5d38b2..6d7ae413d1 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 2d9533cebb..7d4a399e11 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 0d86edcdb4..7dbcaf4823 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 5e2d21c048..f01bcada12 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 4d056a83ed..5aa2b03d4c 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 85a96056af..3283af8c63 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 010/552] 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 a6a865514b..6b12891c57 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 cb64a3b1ba..d3a7629116 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 e993c240da..7f553be1ab 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 0000000000..ae180742fb --- /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 011/552] 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 40c9af9056..2abdc805d4 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 012/552] 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 2fbb78c8f3..9f36fc736a 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 8f384c3d91..9e3dd94f68 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 013/552] 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 06bab375e9..f94b397425 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 d166d5385c..543a863765 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 bd5e83ecc2..acc141baf6 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 db5e8a0012..5e7dc0c0ef 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 caa953ff45..7e7555d732 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 26e6681041..f0c8e0a00f 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 06b69fd042..7e09a4b2f4 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 f4a88b639b..dbb0ecdf9c 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 6f3436410a..5cf659f213 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 9d57ea7d89..3cd57bb82d 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 43d19f9e0b..0f66a94d1a 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 2dd22796cb..b19d31f6cd 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 d2dc1a7c5b..f13c16052f 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 72041da665..ef8c782b39 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 a7677edf96..dc9468def5 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 c4d0415600..52a21837a0 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 017166250c..7674ba7c02 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 014/552] 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 bd335c23d8..6df927c60b 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 015/552] 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 d3a7629116..8d827f5559 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 84fd30a4fe..ff1aa7e98d 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 cec188e352..9937287121 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 016/552] 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 95e50d00f4..4f3823f8a2 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 3c1e4129c3..50d433208b 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 017/552] 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 6df927c60b..b768e6c837 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 73676ffd5d..74c19bf10e 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 d027ce960e..6d6e212047 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 018/552] 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 8d857921ef..55803085f2 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 f29b8d2b2d..75d9bc9b58 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 4d8486b3b4..a8d71f1eed 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 0589b02148..c95114015f 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 70e192356f..e8bd9c11ad 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 89df6456a7..bc0eb42710 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 55b66aaefc..00c92b109d 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 aec4ce5231..f658a15ed1 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 aebd4ef169..94c3645dc7 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 3494def374..ef6dfc5e19 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 c948ee9b7b..a582818eb3 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 8a45a7bf60..2fec233079 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 7b1e58083d..7a887d1835 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 263a571bf3..88d6dc79ba 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 338a83fedc..61197e9f0e 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 9f7d777194..2a4e1bf425 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 364b51b226..5f6482ed94 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 fd112b6e24..aea0ad1fec 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 877e4454c4..d1fbf02f95 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 812f339b9c..365afd407d 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 22d3cff7ad..285108cd3e 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 8f8fea3820..ac921c0e5d 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 7420db144d..fd401b71b0 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 56a3d9034e..f19f02f0c7 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 268bff0d8b..d1c5ffba55 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 19c8876b12..970c4ae9de 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 a53e777d49..6f0048afbb 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 849ea71d8b..4e028a048d 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 19ddea7a14..5ca484884a 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 d466b466a7..1c99237c71 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 06146dcb68..e6e7a48b8d 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 7a44944d24..39aee445f2 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 a4a200c34b..e9dac753dd 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 c27d8e124c..3e468c8f9f 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 f3a0f0fcb8..8817e07a05 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 78e0f66457..07d2ff1376 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 9dec68f032..148a65289d 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 7b2dde1c83..b350cb2806 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 db392c6cc9..99208d4cfe 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 5a8acd513f..4335356356 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 e5f21bc15b..7a1c2d10a1 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 2ac7b40049..f35785b252 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 fd5e1d90c8..6f88e95360 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 59ac8d7ee3..b5a3d3f7e3 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 0f66a94d1a..64a4ff0cae 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 736fa05a76..88a7b8d378 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 019303ebf3..c053c16f28 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 bbf88ee035..5c077bbdad 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 948bc8d5f3..54a470cb30 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 2990a44a5c..99b7dc6f49 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 998bd3bdfa..596c5ef63e 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 17238af074..23c15b8841 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 6a6e6fdc08..0645e41509 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 3283af8c63..c3388ab7dc 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 efcc9f1262..94f6c3b085 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 ce6aa96df8..0a8f7bcc09 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 d483093a76..be5f1f3040 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 d4344f95fc..f629c183c3 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 8d6fe75ae9..a6248d1e3b 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 cca1fe75a7..2f79a91749 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 979ffc391c..4c29d3e106 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 656d7be6bc..c1ae89b320 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 fc0bd3e53e..61c661a58e 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 4b82df67be..505fb41a38 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 034435dd76..d2f7132690 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 ad5928016f..5f9cba0d8c 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 4080a52865..934c3a8dda 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 063559d2e5..1cf3afc002 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 c36b9ed9b3..4ff614e5bd 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 6d96afc78b..d494245b19 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 5607c59a7e..936eea6c25 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 09d6f745bc..08356590cf 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 8d114f200e..74e34c6a42 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 df408910ec..d5dd4f4c95 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 d1c4926a04..a45be61649 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 e4d6c308f5..005f7e2550 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 538ea25278..b9cca8341d 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 c1514d0e9b..7f24290e20 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 019/552] 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 7dbcaf4823..698d76da10 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 c3388ab7dc..54a3db202d 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 020/552] 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 4ca5035edc..8acf08284d 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 f9ea90caf9..9e0fe68fc3 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 0000000000..23f403589d --- /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 a5f262f012..87796539c8 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 42529e1e50..792488c9f9 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 35b1473c56..c0ae7471d9 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 827087c3df..d09b4c5bf0 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 0000000000..5b9292522c --- /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 0000000000..5ec9bc575a --- /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 021/552] 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 ef3ceae62f..45044e0ff6 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 022/552] 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 ae180742fb..0000000000 --- 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 62fb17e4d4..6354850fc0 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 023/552] 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 6b12891c57..035ba46b84 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 fb008dc452..b6f7d655ac 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 f5e8b2363a..ffb5190ca2 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 0000000000..25b5d526ac --- /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 024/552] 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 fd0ac05292..9fabee1e8d 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 ef24e4cdc8..bf243ac431 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 025/552] 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 5f18f01cb0..64c7664826 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 0000000000..a443e40cf8 --- /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 026/552] 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 d07122552c..efd8265941 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 9f36fc736a..efb657d177 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 39bb26a5c8..cbc595651c 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 80d648508d..2f3b7bf2f5 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 027/552] 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 5a9775bad2..dca8c5a4fd 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 028/552] 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 22059c0a8b..270779272c 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 029/552] 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 e37e867daa..14b961ee1e 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 b768e6c837..bfc377a16d 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 74c19bf10e..c94c4323f0 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 6d6e212047..afb51da096 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 87796539c8..1de6d531a8 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 34d76f21e7..1004826eab 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 e288a56c7a..530561cd9c 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 030/552] 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 f002941ec8..5b34bcb3d2 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 458435c1ac..c376ef89ad 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 e994531b62..8143b94d5e 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 031/552] 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 a443e40cf8..0000000000 --- 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 6ddec911fc..1b2602ad00 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 032/552] 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 035ba46b84..bbc83ff254 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 47df3d24f6..3e859d3d7f 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 5b19a8ec00..76052ec3b1 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 64a4ff0cae..1b6fb4e9f0 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 033/552] 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 bbc83ff254..c6a1d17d94 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 35afd95ad3..5b9893e75b 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 034/552] 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 8de4430c1d..195da10082 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 035/552] 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 f6c1b69ff7..4242cd1e3b 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 036/552] 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 4f22388f36..855289e846 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 037/552] 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 dca8c5a4fd..65104e1fef 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 038/552] 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 698d76da10..8d16edc617 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 f01bcada12..e4e192fa38 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 5aa2b03d4c..52125b52ca 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 a95fac0bd9..23d1920097 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 54a3db202d..7acb23c5bf 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 039/552] 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 97c3a46c55..7484640615 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 c15f6dc1ff..9b7e324667 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 601508e2e5..a95c13fb55 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 0028ac4fba..b1418a3ecc 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 0000000000..ae752165a7 --- /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 d19f40c39c..a1bb63d46e 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 0000000000..1daf32aa5d --- /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 cddd43f6e1..b153fd2ba7 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 040/552] 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 b6f7d655ac..29c137b635 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 0000000000..978e3e94d5 --- /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 041/552] 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 65104e1fef..dca8c5a4fd 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 042/552] 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 34c9634108..d1837ac14d 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 043/552] 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 804481cd9f..a19ad06719 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 044/552] 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 04544ffb77..4764ce05fc 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 045/552] 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 9c198e6fa5..9cac8e2321 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 046/552] 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 a7ba3143f2..a498a95253 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 bfd153143b..fa7a2d86d5 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 047/552] 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 a95c13fb55..390aac00b1 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 048/552] 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 9cac8e2321..bc447eb670 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 049/552] 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 18b09e4c03..c387e14e13 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 050/552] 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 45044e0ff6..53b2832f05 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 005f7e2550..c1e5638066 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 051/552] 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 8acf08284d..ccf4fe4972 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 53b2832f05..b8a8d21b09 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 142fec0569..ab12bad1e5 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 a0c16f2ed4..5c5d350932 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 48088a6093..43dfc69ae7 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 4b8755ab48..b521486686 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 3274462d70..9bfa7001a9 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 218a597d8d..691d33a2c5 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 91221ea498..1f75b77592 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 1a33f6a27b..60434e4b8b 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 cb2ecee79e..0000000000 --- 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 ef609e8c87..546e495c56 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 0000000000..99ce183f2b --- /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 a055e16b73..645c03b2b8 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 8b3a9bf556..0000000000 --- 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 34c11e06ca..76a063faae 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 4f3823f8a2..8eaa9e1e2a 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 512fb6190f..5ffa25e531 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 fba032c1ec..3c0aabe9a6 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 e9e704075a..37405d8d96 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 71003371e8..d700ab9a73 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 c94c4323f0..868198268c 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 e3c54eeae3..5544668ccf 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 132f3788d3..a8a9c4df8b 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 23f403589d..d0ef45d04d 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 1de6d531a8..4a013f8fc1 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 f266d2a6b7..997a10cd83 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 e95f3799c5..d5ae5a21df 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 a6660e14c7..da38a6fc29 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 8083ae179a..f8765c4dc8 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 9fe934f890..15727c9f7b 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 052/552] 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 c6a1d17d94..b10b9607ab 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 2067839291..880ccdff11 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 053/552] 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 14b961ee1e..87b38092e4 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 054/552] 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 667f27e95d..da72f2fab0 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 520b27e263..9c92877848 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 055/552] 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 9eccdef456..1e5772189c 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 9c92877848..b8a2c2c6a4 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 056/552] 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 da72f2fab0..dde4be1282 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 b8a2c2c6a4..17640387af 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 057/552] 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 f2e4848df9..029e9abf8c 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 148a65289d..47a8d327b8 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 058/552] 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 01a79c1e36..1691e7ccfc 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 530561cd9c..874ad66d63 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 0000000000..e69de29bb2 diff --git a/tests/newconfig/bar/mfoo.nim b/tests/newconfig/bar/mfoo.nim new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/newconfig/bar/mfoo.nim.cfg b/tests/newconfig/bar/mfoo.nim.cfg new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/newconfig/bar/mfoo.nims b/tests/newconfig/bar/mfoo.nims new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/newconfig/bar/nim.cfg b/tests/newconfig/bar/nim.cfg new file mode 100644 index 0000000000..e69de29bb2 From cf714c129f7dd598863d1cc588e685df2438c658 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 2 Jan 2021 21:47:26 +0100 Subject: [PATCH 059/552] 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 874ad66d63..3f5b10f974 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 060/552] 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 3f5b10f974..874ad66d63 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 061/552] 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 695f446460..a4e7279091 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 062/552] 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 29c137b635..618fd5dd76 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 063/552] 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 680b61461d..5ce9bf2fb7 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 064/552] 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 874ad66d63..63aac0ede9 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 065/552] 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 a4e7279091..113939567c 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 066/552] 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 618fd5dd76..e220ba7a39 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 508715905e..cb879d3b3d 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 067/552] 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 7aa8857946..9d41a53899 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 94f6c3b085..385e6e651b 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 068/552] 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 367e693e92..8c419caacb 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 069/552] 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 1b6fb4e9f0..4176f70e8b 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 070/552] 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 b10b9607ab..11ac338729 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 d083e20b9a..7fd8b1fc9e 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 071/552] 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 11ac338729..b97fcdd037 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 063fad8b45..a9d0ed4cb0 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 072/552] 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 761e0dd69b..64ec9cda3f 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 c6a2eadb19..024175cbf1 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 073/552] 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 8817e07a05..41c0601380 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 074/552] 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 b521486686..9fb9b7e6e5 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 868198268c..4c67aaea72 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 c4f15a3360..258558c3f6 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 b5d275e25d..7553f921b9 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 d9b3f43886..c0b8bb030e 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 0000000000..c0b8bb030e --- /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 0000000000..6cbc5c6990 --- /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 0000000000..e5291969dd --- /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 075/552] 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 c894bc9f99..39f0aaa27a 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 a7f0fd4bbe..58fe7bf4f8 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 7b1a25bf08..5686da81c0 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 4176f70e8b..16c2740322 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 0000000000..8d0dd57e3b --- /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 0000000000..7a0ad7985d --- /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 076/552] 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 219b1bed55..76b948e6a2 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 d4e8ce86a4..59d1381c7f 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 851ec0c5f3..f7d37f4bff 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 5b9893e75b..87dd9eeec8 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 2e2bd2402a..3af33b5354 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 15727c9f7b..8220013c12 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 f5c3cc1ac8..7be7221bc9 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 0cfedce473..81117dd522 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 077/552] 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 f3f59baf8b..4500d994ef 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 078/552] 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 16c2740322..a4b493b938 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 978e3e94d5..0000000000 --- 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 079/552] 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 a4b493b938..fbc6b80ad8 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 080/552] 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 4500d994ef..d9f5a3a0f9 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 081/552] 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 8a5ab81f05..0000000000 --- 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 2a56432768..0000000000 --- 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 a594c0fa8c..0000000000 --- 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 d94da8c1f0..0000000000 --- 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 3c43a48c96..0000000000 --- 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 fb7cda984e..0000000000 --- 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 42446faead..0000000000 --- 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 983cd555fb..0000000000 --- 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 2dde517903..0000000000 --- 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 6e3d0c2c39..0000000000 --- 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 54e00884f9..0000000000 --- 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 77d9638343..0000000000 --- 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 b3c7bb9712..0000000000 --- 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 8bf3450c9e..0000000000 --- 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 e8ff8a9527..0000000000 --- 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 082/552] 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 b97fcdd037..306cf54d31 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 76052ec3b1..a6a3676b99 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 fbc6b80ad8..e5cb58ebab 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 083/552] 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 04e5e8e56d..b9371c1e17 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 8c9fa3055d..15267b9051 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 084/552] 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 390aac00b1..cdef7c8048 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 b1418a3ecc..a25df9e420 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 ae752165a7..3fa0dc1d37 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 1daf32aa5d..ba3b1de684 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 085/552] 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 abddaf4cbd..1a895bfa8d 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 f58acf1c8d..5bcd1ea7c0 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 086/552] 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 019b735f58..e7bbcecc23 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 087/552] 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 5c5d350932..a995804c73 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 80f54caee1..df4c3771af 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 088/552] 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 24935f511d..4dca024c17 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 28f05ecbe0..f56c327196 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 089/552] 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 7484640615..204123f419 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 090/552] 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 f6d0f945e7..7614b9d20b 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 091/552] 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 186b23cd95..73929f8132 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 23aa2d7271..b67440b9e3 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 092/552] [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 131f32f321..55f2b4a0f8 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 675a24c923..4b369210d6 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 0000000000..db7ebb065f --- /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 093/552] 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 3e5695360d..57d13fde35 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 208f0fcaaa..7fdf40e4b3 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 094/552] 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 4dca024c17..b9e47bd709 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 f56c327196..06d01a9fba 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 095/552] 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 d9f5a3a0f9..29a6953794 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 096/552] 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 306cf54d31..4e9b8c1622 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 3e859d3d7f..504a352b50 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 a6a3676b99..bcda68afe1 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 e5cb58ebab..62fdcd19f6 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 097/552] 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 d3a7c4fb64..957a8193a1 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 0000000000..f162dbe57c --- /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 098/552] 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 4e9b8c1622..afe49f1f31 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 0000000000..5c882858db --- /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 0000000000..0cf952810c --- /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 099/552] [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 5da2628847..95c9c6161d 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 100/552] 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 f0d7668a7c..013e8d38e3 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 101/552] 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 ccf4fe4972..de4ab5b77f 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 b8a8d21b09..440ecdc7fb 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 ab12bad1e5..798eaf7f9a 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 9fb9b7e6e5..adb85bdd72 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 691d33a2c5..a85314ac2f 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 546e495c56..cbfb0c5d1a 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 99ce183f2b..7252621202 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 1e8c912d9a..d2a84fd36e 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 645c03b2b8..0fde13eb93 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 4b369210d6..f588f95558 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 3c0aabe9a6..cf6f07b7c6 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 d700ab9a73..e79be67767 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 5544668ccf..ab3ef5ab81 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 a8a9c4df8b..deb0174b5e 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 b931298527..0170c99492 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 a781f1d519..4af0c28fa9 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 997a10cd83..e3885540e5 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 78c79bf599..24e26b2b7b 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 a79a471de9..c1627fe0ca 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 d5ae5a21df..6f3b158676 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 da38a6fc29..f2f0744473 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 1e5772189c..4a04cfb6d0 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 7e8fffc01b..93184c568d 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 1f633549a0..dfbb022c89 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 3f1cdace34..d273cac142 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 c2c59bb31e..a76116b1c4 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 14c3b9a119..65dc959167 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 99c588657d..64113a4c61 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 9a16cc3e07..156bc66d79 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 73929f8132..560d20b3fe 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 461db9e89b..e9ab6b47db 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 102/552] 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 ba944ba813..11c3245484 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 103/552] 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 4a013f8fc1..6cdb5db634 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 104/552] 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 4c67aaea72..438f90ba6e 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 105/552] 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 0000000000..8ad96f0dac --- /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 106/552] 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 3093f55648..70f3327b14 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 107/552] 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 81117dd522..75bb4443fc 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 108/552] 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 74ca804f32..fcc89dfc16 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 75bb4443fc..f258087e71 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 109/552] 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 f73df5e5f6..a8fc78e391 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 110/552] 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 0000000000..0275b1b70d --- /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 111/552] 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 29a6953794..86688d4b5f 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 112/552] 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 afe49f1f31..0705d00909 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 70f3327b14..ea847ef8d9 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 9287e9864d..774de1260e 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 6a634d90fc..4881370eec 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 113/552] 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 ea847ef8d9..6acf8d9cd1 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 114/552] 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 3d0a9d0ae1..4cc2dcba02 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 4ff363075c..fa82acc3bf 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 115/552] 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 5686da81c0..314a839362 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 116/552] 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 64113a4c61..45f20da9bf 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 117/552] 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 0f75faee33..b8df456e09 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 6d6c552508..eab18f417d 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 46931eb546..6790276a99 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 e220ba7a39..25184eb15e 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 17640387af..ce7bb7d8c2 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 4d5a15940e..fcbacc5339 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 118/552] 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 560d20b3fe..283690080b 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 0000000000..97a225f168 --- /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 119/552] 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 5060deb78d..c8cd839be2 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 120/552] 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 f588f95558..10a33423e1 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 7070e6c3f8..353992fcac 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 62fdcd19f6..edab62a660 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 121/552] 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 b8df456e09..00f0aca7c1 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 ce71344d06..747cf0b8d1 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 122/552] 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 0000000000..fdb5735886 --- /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 123/552] 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 0000000000..29fb903a45 --- /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 124/552] 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 58fe7bf4f8..48fe516581 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 4ad17fa951..0000000000 --- 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 125/552] 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 0000000000..c9f6883090 --- /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 126/552] 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 d21db76343..f07adc4289 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 585b3cc49c..ff4dac7811 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 4f06de4554..01e2d2ee9b 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 e383bd8d05..c002ddb836 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 090032b98a..7f3fa8cf20 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 ee6e0ea172..d33a6066e0 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 805a84eb70..8146562b6a 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 40346ecafe..52704411fc 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 29b9a4131f..4455afcbe7 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 6dc65b1cd5..7e9d71da39 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 27926b14ff..dcafaf45f4 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 0eb70e143a..2456b25fde 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 c92e298129..1eb02d7852 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 2a4c2c03c8..3202c5a53a 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 c6de8cf7ce..e34993e3ef 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 b76839842b..cf2e0c2470 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 10b59a74e7..c7c045683a 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 4a31524f6a..3bae6a00bb 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 ea173691f5..aad889591d 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 62064fe69b..d105346da2 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 791ead1624..5b0fe0dbb7 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 adb5e6f1fb..3ccb47cc85 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 aa13d134ab..f81637d734 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 8db7c233b1..509f72e8a7 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 5db68bfa70..04c966ffe1 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 f231cdc7d6..e0044e1ca9 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 171b2f9188..d2d6f8fe7d 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 e0d1bdb32b..3aef6bb0f2 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 b0a3d8232b..358a9b45e4 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 5e4b171fe7..d6cbe1a35f 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 535/552] 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 4080f789c2..219a66ae7a 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 b8a361af14..bf43adddd4 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 536/552] 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 37831c72c2..57d5e7b013 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 53088dfda6..cb80f2873c 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 dbdcca79ee..48b3094e7e 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 537/552] 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 0000000000..33108c50c4 --- /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 538/552] 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 69f6a6ffbf..3bbc8a6b67 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 539/552] 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 d824205190..50a2fb58c8 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 9c751b1ca1..73ee9cb8a2 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 6a128466a5..3678adacf8 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 52a4a3339b..88b2a94772 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 c7d66465d4..0918fc3799 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 44902143df..99a68e0f03 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 05c4730901..61aa0e697f 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 d20ed8e267..b653912526 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 889c65cc06..980e77e4be 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 96a3824538..d8f82aea0f 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 8959236063..b61cdcadb1 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 b8a8b3e2cb..de3773ca5e 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 7503d4da27..7d7a2b6f72 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 b384dad243..bbe40507f6 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 61bcc424bb..54ed51dbc8 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 b75b0cf9ac..57e3c79896 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 f0c3da5173..5740431259 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 0000000000..6c85612e25 --- /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 8f7e2cbc72..06fa450977 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 42c4488486..04e10ba04c 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 a3b6db57d2..1dd7c69cad 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 58851b5c4c..ac34f1f85e 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 c29a0820c7..7ead7c8bac 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 ebadb874dd..0a07e6b766 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 540/552] 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 cf58b3b816..32c717c561 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 8f4dd87a9c..621dc643f3 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 541/552] 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 36a2115456..510f0682d9 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 542/552] [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 6bedf2de25..73b53e3d68 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 543/552] 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 73af232b4c..c62ac633fa 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 f7d37f4bff..a1ceff5b42 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 0734d891a6..937e3727ba 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 21bb9e1a05..ccf14080bf 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 544/552] 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 510f0682d9..4098749ce6 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 545/552] 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 0000000000..89957e1d31 --- /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 546/552] 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 3202c5a53a..9d715b1c7c 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 8fe1fa603f..e376ad87fe 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 547/552] 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 32c717c561..836b3512a3 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 548/552] 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 219a66ae7a..cc4f54fe02 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 7be7221bc9..7d51db6463 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 549/552] 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 ea00176b53..779f1a91ff 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 550/552] 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 9d715b1c7c..62e02815c6 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 551/552] 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 5688732692..4bb9f3e6be 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 552/552] 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 57e3c79896..377602e75d 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 0000000000..d54977b4e2 --- /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 6c85612e25..d3c003ff7a 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 04e10ba04c..103c8d343e 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