From 0c1d595fae32b41085177ee8bd95b7845c7db68c Mon Sep 17 00:00:00 2001 From: Hiroki Noda Date: Tue, 21 Mar 2023 02:41:25 +0900 Subject: [PATCH 01/55] NuttX: use accept4 (#21544) NuttX supports accept4 since https://github.com/apache/nuttx/commit/48c9d1033659603663f6e35587cf27045a130e0d --- lib/posix/posix.nim | 2 +- lib/posix/posix_other.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 12fc8fd571..fbe945df33 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -958,7 +958,7 @@ proc `==`*(x, y: SocketHandle): bool {.borrow.} proc accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen): SocketHandle {. importc, header: "", sideEffect.} -when defined(linux) or defined(bsd): +when defined(linux) or defined(bsd) or defined(nuttx): proc accept4*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen, flags: cint): SocketHandle {.importc, header: "".} diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 1b6734b518..d5e3c782e5 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -646,7 +646,7 @@ elif defined(nuttx): else: var SO_REUSEPORT* {.importc, header: "".}: cint -when defined(linux) or defined(bsd): +when defined(linux) or defined(bsd) or defined(nuttx): var SOCK_CLOEXEC* {.importc, header: "".}: cint when defined(macosx): From ae06c6623d3bca0f3eb080b4e6f18d2055ca9351 Mon Sep 17 00:00:00 2001 From: Hiroki Noda Date: Tue, 21 Mar 2023 02:43:10 +0900 Subject: [PATCH 02/55] NuttX: use posix_spawn for osproc (#21539) NuttX has standard posix_spawn interface, and can be used with it. * https://nuttx.apache.org/docs/12.0.0/reference/user/01_task_control.html#c.posix_spawn --- lib/posix/posix_other.nim | 10 +++++----- lib/pure/osproc.nim | 10 ++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index d5e3c782e5..ea8731405d 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,7 +10,7 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -when defined(freertos) or defined(zephyr) or defined(nuttx): +when defined(freertos) or defined(zephyr): const hasSpawnH = false # should exist for every Posix system nowadays hasAioH = false @@ -675,14 +675,14 @@ when defined(haiku): when hasSpawnH: when defined(linux): - # better be safe than sorry; Linux has this flag, macosx doesn't, don't - # know about the other OSes + # better be safe than sorry; Linux has this flag, macosx and NuttX don't, + # don't know about the other OSes - # Non-GNU systems like TCC and musl-libc don't define __USE_GNU, so we + # Non-GNU systems like TCC and musl-libc don't define __USE_GNU, so we # can't get the magic number from spawn.h const POSIX_SPAWN_USEVFORK* = cint(0x40) else: - # macosx lacks this, so we define the constant to be 0 to not affect + # macosx and NuttX lack this, so we define the constant to be 0 to not affect # OR'ing of flags: const POSIX_SPAWN_USEVFORK* = cint(0) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 915337f122..e30f1da737 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1053,13 +1053,15 @@ elif not defined(useNimRtl): var mask: Sigset chck sigemptyset(mask) chck posix_spawnattr_setsigmask(attr, mask) - if poDaemon in data.options: - chck posix_spawnattr_setpgroup(attr, 0'i32) + when not defined(nuttx): + if poDaemon in data.options: + chck posix_spawnattr_setpgroup(attr, 0'i32) var flags = POSIX_SPAWN_USEVFORK or POSIX_SPAWN_SETSIGMASK - if poDaemon in data.options: - flags = flags or POSIX_SPAWN_SETPGROUP + when not defined(nuttx): + if poDaemon in data.options: + flags = flags or POSIX_SPAWN_SETPGROUP chck posix_spawnattr_setflags(attr, flags) if not (poParentStreams in data.options): From 741fed716edb163c95475db79b5a1ec95de11fa6 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 21 Mar 2023 04:48:13 +1100 Subject: [PATCH 03/55] Use `analyseIfAddressTaken` logic for checking if address is taken in converter (#21533) * Add a test case There are way more test cases (See all branches of analyseIfAddressTaken but this covers at least a second branch * Port analyseIfAddressTaken from semexprs to sigmatch This was done since we cannot import sem or semexprs (circular import) but we need the rest of the logic. In needs to be done here since the converter isn't semmed afterwards and so we can't just leave the process til later use the version from semexprs * Less hacky solution which has the checking be done in analyseIfAddressTakenInCall This was done instead of the recommendation on removing it since sfAddrTaken is used in places other than the backend * Remove weird whitespace * Still check nkHiddenAddr if we are checking a converter --- compiler/semexprs.nim | 24 +++++++++++++++--------- compiler/sigmatch.nim | 3 +-- tests/converter/t21531.nim | 10 ++++++++++ 3 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 tests/converter/t21531.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cfa34fcdc2..f74a72692b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -787,7 +787,7 @@ proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode = else: result = newHiddenAddrTaken(c, n, isOutParam) -proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = +proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) = checkMinSonsLen(n, 1, c.config) const FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl, @@ -795,10 +795,15 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove, mWasMoved} + template checkIfConverterCalled(c: PContext, n: PNode) = + ## Checks if there is a converter call which wouldn't be checked otherwise + # Call can sometimes be wrapped in a deref + let node = if n.kind == nkHiddenDeref: n[0] else: n + if node.kind == nkHiddenCallConv: + analyseIfAddressTakenInCall(c, node, true) # get the real type of the callee # it may be a proc var with a generic alias type, so we skip over them var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}) - if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams: # BUGFIX: check for L-Value still needs to be done for the arguments! # note sometimes this is eval'ed twice so we check for nkHiddenAddr here: @@ -813,6 +818,8 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = discard "allow access within a cast(unsafeAssign) section" else: localError(c.config, it.info, errVarForOutParamNeededX % $it) + # Make sure to still check arguments for converters + c.checkIfConverterCalled(n[i]) # bug #5113: disallow newSeq(result) where result is a 'var T': if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: var arg = n[1] #.skipAddr @@ -824,15 +831,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = return for i in 1.. Date: Mon, 20 Mar 2023 18:49:18 +0100 Subject: [PATCH 04/55] Add check for nimMaxJeap on occupied memory + allocation size (#21521) * fix nimMAxHeap checks * move check to alloc pages * remove debug trace * Fix bad indentation How the hell did that pass through CI ? --- lib/system/alloc.nim | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index cee70f6774..ac1741ceb5 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -268,6 +268,20 @@ proc getMaxMem(a: var MemRegion): int = # maximum of these both values here: result = max(a.currMem, a.maxMem) +const nimMaxHeap {.intdefine.} = 0 + +proc allocPages(a: var MemRegion, size: int): pointer = + when nimMaxHeap != 0: + if a.occ + size > nimMaxHeap * 1024 * 1024: + raiseOutOfMem() + osAllocPages(size) + +proc tryAllocPages(a: var MemRegion, size: int): pointer = + when nimMaxHeap != 0: + if a.occ + size > nimMaxHeap * 1024 * 1024: + raiseOutOfMem() + osTryAllocPages(size) + proc llAlloc(a: var MemRegion, size: int): pointer = # *low-level* alloc for the memory managers data structures. Deallocation # is done at the end of the allocator's life time. @@ -277,7 +291,7 @@ proc llAlloc(a: var MemRegion, size: int): pointer = # is one page: sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6" var old = a.llmem # can be nil and is correct with nil - a.llmem = cast[PLLChunk](osAllocPages(PageSize)) + a.llmem = cast[PLLChunk](allocPages(a, PageSize)) when defined(nimAvlcorruption): trackLocation(a.llmem, PageSize) incCurrMem(a, PageSize) @@ -453,15 +467,10 @@ when false: it, it.next, it.prev, it.size) it = it.next -const nimMaxHeap {.intdefine.} = 0 - proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = when not defined(emscripten): if not a.blockChunkSizeIncrease: let usedMem = a.occ #a.currMem # - a.freeMem - when nimMaxHeap != 0: - if usedMem > nimMaxHeap * 1024 * 1024: - raiseOutOfMem() if usedMem < 64 * 1024: a.nextChunkSize = PageSize*4 else: @@ -470,11 +479,11 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = var size = size if size > a.nextChunkSize: - result = cast[PBigChunk](osAllocPages(size)) + result = cast[PBigChunk](allocPages(a, size)) else: - result = cast[PBigChunk](osTryAllocPages(a.nextChunkSize)) + result = cast[PBigChunk](tryAllocPages(a, a.nextChunkSize)) if result == nil: - result = cast[PBigChunk](osAllocPages(size)) + result = cast[PBigChunk](allocPages(a, size)) a.blockChunkSizeIncrease = true else: size = a.nextChunkSize @@ -654,7 +663,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk = releaseSys a.lock proc getHugeChunk(a: var MemRegion; size: int): PBigChunk = - result = cast[PBigChunk](osAllocPages(size)) + result = cast[PBigChunk](allocPages(a, size)) when RegionHasLock: if not a.lockActive: a.lockActive = true @@ -811,9 +820,9 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") var size = roundup(requestedSize, MemAlign) sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small") - sysAssert(size >= requestedSize, "insufficient allocated size!") #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size) + if size <= SmallChunkSize-smallChunkOverhead(): # allocate a small block: for small chunks, we use only its next pointer let s = size div MemAlign From fb00b482eb1ed685c93034360467ee76238e6548 Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Mon, 20 Mar 2023 19:49:59 +0200 Subject: [PATCH 05/55] Avoid calling build_all* when nim binary is present (#21522) - `nimble` will build `nim` using `bin/nim` and if it is already present we can reuse it. --- nim.nimble | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nim.nimble b/nim.nimble index 380ffbce8f..b0253f66d5 100644 --- a/nim.nimble +++ b/nim.nimble @@ -10,6 +10,8 @@ skipDirs = @["build" , "changelogs" , "ci" , "csources_v2" , "drnim" , "nimdoc", before install: when defined(windows): - exec "build_all.bat" + if not "bin\nim.exe".fileExists: + exec "build_all.bat" else: - exec "./build_all.sh" + if not "bin/nim".fileExists: + exec "./build_all.sh" From 285ea3c48e7b01fe6beecf794e9e8cc904c27889 Mon Sep 17 00:00:00 2001 From: Mark Leyva Date: Mon, 20 Mar 2023 10:50:58 -0700 Subject: [PATCH 06/55] Fix: #21541. Add support for xnVerbatimText (#21542) to text and text= procs. Remove unnecessary LF for xnVerbatimText in $ proc. --- lib/pure/xmltree.nim | 8 ++++---- tests/stdlib/txmltree.nim | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 82513bc984..186da4df81 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -190,7 +190,7 @@ proc text*(n: XmlNode): lent string {.inline.} = assert $c == "" assert c.text == "my comment" - n.expect {xnText, xnComment, xnCData, xnEntity} + n.expect {xnText, xnVerbatimText, xnComment, xnCData, xnEntity} result = n.fText proc `text=`*(n: XmlNode, text: sink string) {.inline.} = @@ -208,7 +208,7 @@ proc `text=`*(n: XmlNode, text: sink string) {.inline.} = e.text = "a new entity text" assert $e == "&a new entity text;" - n.expect {xnText, xnComment, xnCData, xnEntity} + n.expect {xnText, xnVerbatimText, xnComment, xnCData, xnEntity} n.fText = text proc tag*(n: XmlNode): lent string {.inline.} = @@ -735,7 +735,7 @@ proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2, addNewLines = true, lastNodeIsText = false) = proc noWhitespace(n: XmlNode): bool = for i in 0 ..< n.len: - if n[i].kind in {xnText, xnEntity}: return true + if n[i].kind in {xnText, xnVerbatimText, xnEntity}: return true proc addEscapedAttr(result: var string, s: string) = # `addEscaped` alternative with less escaped characters. @@ -784,7 +784,7 @@ proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2, var lastNodeIsText = false for i in 0 ..< n.len: result.addImpl(n[i], indentNext, indWidth, addNewLines, lastNodeIsText) - lastNodeIsText = n[i].kind == xnText + lastNodeIsText = (n[i].kind == xnText) or (n[i].kind == xnVerbatimText) if not n.noWhitespace(): result.addIndent(indent, addNewLines) diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim index 4362f5ec32..c878715445 100644 --- a/tests/stdlib/txmltree.nim +++ b/tests/stdlib/txmltree.nim @@ -99,3 +99,18 @@ block: # bug #21290 doAssert s == """ Hola """ + +block: #21541 + let root = <>root() + root.add <>child(newText("hello")) + root.add <>more(newVerbatimText("hola")) + let s = $root + doAssert s == """ + hello + hola +""" + + let temp = newVerbatimText("Hello!") + doAssert temp.text == "Hello!" + temp.text = "Hola!" + doAssert temp.text == "Hola!" From da7833c68bd8a3fea4b380e2a0e84753812450fe Mon Sep 17 00:00:00 2001 From: "Eric N. Vander Weele" Date: Mon, 20 Mar 2023 13:51:31 -0400 Subject: [PATCH 07/55] fixes #21538; expand len template parameter once in newSeqWith (#21543) `len` could contain side effects and may result in different values when substituted twice in the template expansion. Instead, capture the result of substituting `len` once. closes: #21538 --- lib/pure/collections/sequtils.nim | 6 +++--- tests/stdlib/tsequtils.nim | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 1773e827b1..bcdd0879d6 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -1077,9 +1077,9 @@ template newSeqWith*(len: int, init: untyped): untyped = import std/random var seqRand = newSeqWith(20, rand(1.0)) assert seqRand[0] != seqRand[1] - - var result = newSeq[typeof(init)](len) - for i in 0 ..< len: + let newLen = len + var result = newSeq[typeof(init)](newLen) + for i in 0 ..< newLen: result[i] = init move(result) # refs bug #7295 diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 176c00214b..2b9ef5d6ea 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -388,6 +388,11 @@ block: # newSeqWith tests seq2D[0][1] = true doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] +block: # bug #21538 + var x: seq[int] = @[2, 4] + var y = newSeqWith(x.pop(), true) + doAssert y == @[true, true, true, true] + block: # mapLiterals tests let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) doAssert x is array[4, int] From 9df8ca0d8104c5f474dd5184b69446bbb1515242 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Mon, 20 Mar 2023 17:51:58 +0000 Subject: [PATCH 08/55] Add URI parsing warning (#21547) Related to CVE-2021-41259 https://github.com/nim-lang/security/security/advisories/GHSA-3gg2-rw3q-qwgc https://github.com/nim-lang/Nim/pull/19128#issuecomment-1181944367 --- lib/pure/httpclient.nim | 2 ++ lib/pure/uri.nim | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index d2cf64149f..fd0ef38564 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -10,6 +10,8 @@ ## This module implements a simple HTTP client that can be used to retrieve ## webpages and other data. ## +## .. warning:: Validate untrusted inputs: URI parsers and getters are not detecting malicious URIs. +## ## Retrieving a website ## ==================== ## diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index ebc8b90efb..725d5bbd95 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -14,6 +14,8 @@ ## as a locator, a name, or both. The term "Uniform Resource Locator" ## (URL) refers to the subset of URIs. ## +## .. warning:: URI parsers in this module do not perform security validation. +## ## # Basic usage From 274d61865f53e4146518a1f54465785595052ffe Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 21 Mar 2023 05:42:57 +0800 Subject: [PATCH 09/55] closes #21536; fixes manual (#21552) fixes manual --- doc/manual.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 31c620ca73..98f9d2ec3e 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -5455,7 +5455,7 @@ more complex type classes: ```nim # create a type class that will match all tuple and object types - type RecordType = tuple or object + type RecordType = (tuple or object) proc printFields[T: RecordType](rec: T) = for key, value in fieldPairs(rec): @@ -5504,7 +5504,7 @@ A type class can be used directly as the parameter's type. ```nim # create a type class that will match all tuple and object types - type RecordType = tuple or object + type RecordType = (tuple or object) proc printFields(rec: RecordType) = for key, value in fieldPairs(rec): From c155e20796a6c1d2d290f76cbc55c3fe872ff86b Mon Sep 17 00:00:00 2001 From: Peter Munch-Ellingsen Date: Mon, 20 Mar 2023 22:43:42 +0100 Subject: [PATCH 10/55] Fix infinite recursion introduced in 7031ea6 [backport 1.6] (#21555) Fix infinite recursion introduced in 7031ea6 --- nimsuggest/nimsuggest.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index ce9681975a..685dbedb8d 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -234,7 +234,7 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos)) proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, graph: ModuleGraph) = - executeNoHooks(cmd, file, dirtyfile, line, col, graph) + executeNoHooks(cmd, file, dirtyfile, line, col, "", graph) proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; tag: string, graph: ModuleGraph) = From f7e3af0c2d68035a649cf9449cc4e02a7ea59e84 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Mar 2023 22:53:13 +0100 Subject: [PATCH 11/55] =?UTF-8?q?mitigates=20#21272;=20but=20it's=20not=20?= =?UTF-8?q?the=20final=20fix=20because=20the=20first=20round=20=E2=80=A6?= =?UTF-8?q?=20(#21462)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mitigates #21272; but it's not the final fix because the first round of overload resolution should already match --- compiler/semcall.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 54f03026f8..987fd4a13b 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -338,7 +338,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = if n[0].kind in nkIdentKinds: let ident = considerQuotedIdent(c, n[0], n).s localError(c.config, n.info, errUndeclaredRoutine % ident) - else: + else: localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree) return @@ -630,7 +630,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, if efExplain notin flags: # repeat the overload resolution, # this time enabling all the diagnostic output (this should fail again) - discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) + result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) elif efNoUndeclared notin flags: notFoundError(c, n, errors) From c814c4d993675551ecf388b6a583c471a1b8bc5e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 21 Mar 2023 22:22:07 +0800 Subject: [PATCH 12/55] fixes #3770; templates with untyped parameters resolve private fields wrongly in generics (#21554) * fixes #3770; templates with untyped parameters resolve private fields wrongly * add a test case for #3770 * rename to `nfSkipFieldChecking` --- compiler/ast.nim | 4 ++-- compiler/sem.nim | 8 ++++---- compiler/semgnrc.nim | 22 ++++++++++++++++++++++ compiler/semobjconstr.nim | 4 ++-- compiler/semtypes.nim | 4 ++-- compiler/vmgen.nim | 2 +- tests/generics/m3770.nim | 6 ++++++ tests/generics/t3770.nim | 9 +++++++++ 8 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 tests/generics/m3770.nim create mode 100644 tests/generics/t3770.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index f93c8d9101..b5306c423c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -510,7 +510,7 @@ type nfLastRead # this node is a last read nfFirstWrite # this node is a first write nfHasComment # node has a comment - nfUseDefaultField # node has a default value (object constructor) + nfSkipFieldChecking # node skips field visable checking TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46) @@ -1081,7 +1081,7 @@ const nfIsRef, nfIsPtr, nfPreventCg, nfLL, nfFromTemplate, nfDefaultRefsParam, nfExecuteOnReload, nfLastRead, - nfFirstWrite, nfUseDefaultField} + nfFirstWrite, nfSkipFieldChecking} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 diff --git a/compiler/sem.nim b/compiler/sem.nim index 48a7d56c8a..1c15f905e3 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -572,7 +572,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s let asgnExpr = defaultNodeField(c, recNode, recNode.typ) if asgnExpr != nil: hasDefault = true - asgnExpr.flags.incl nfUseDefaultField + asgnExpr.flags.incl nfSkipFieldChecking result.add newTree(nkExprColonExpr, recNode, asgnExpr) return @@ -582,7 +582,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s newSymNode(getSysMagic(c.graph, recNode.info, "zeroDefault", mZeroDefault)), newNodeIT(nkType, recNode.info, asgnType) ) - asgnExpr.flags.incl nfUseDefaultField + asgnExpr.flags.incl nfSkipFieldChecking asgnExpr.typ = recType result.add newTree(nkExprColonExpr, recNode, asgnExpr) else: @@ -604,7 +604,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] = defaultValue = newIntNode(nkIntLit#[c.graph]#, 0) defaultValue.typ = discriminator.typ selectedBranch = recNode.pickCaseBranchIndex defaultValue - defaultValue.flags.incl nfUseDefaultField + defaultValue.flags.incl nfSkipFieldChecking result.add newTree(nkExprColonExpr, discriminator, defaultValue) result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1]) of nkSym: @@ -616,7 +616,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] = let asgnExpr = defaultNodeField(c, recNode, recType) if asgnExpr != nil: asgnExpr.typ = recType - asgnExpr.flags.incl nfUseDefaultField + asgnExpr.flags.incl nfSkipFieldChecking result.add newTree(nkExprColonExpr, recNode, asgnExpr) else: doAssert false diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 0827e68456..fa37af850a 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -78,14 +78,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, if macroToExpandSym(s): onUse(n.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) + c.friendModules.add(s.owner.getModule) result = semGenericStmt(c, result, {}, ctx) + discard c.friendModules.pop() else: result = symChoice(c, n, s, scOpen) of skMacro: if macroToExpandSym(s): onUse(n.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) + c.friendModules.add(s.owner.getModule) result = semGenericStmt(c, result, {}, ctx) + discard c.friendModules.pop() else: result = symChoice(c, n, s, scOpen) of skGenericParam: @@ -245,7 +249,9 @@ proc semGenericStmt(c: PContext, n: PNode, if macroToExpand(s) and sc.safeLen <= 1: onUse(fn.info, s) result = semMacroExpr(c, n, n, s, {efNoSemCheck}) + c.friendModules.add(s.owner.getModule) result = semGenericStmt(c, result, flags, ctx) + discard c.friendModules.pop() else: n[0] = sc result = n @@ -254,7 +260,9 @@ proc semGenericStmt(c: PContext, n: PNode, if macroToExpand(s) and sc.safeLen <= 1: onUse(fn.info, s) result = semTemplateExpr(c, n, s, {efNoSemCheck}) + c.friendModules.add(s.owner.getModule) result = semGenericStmt(c, result, flags, ctx) + discard c.friendModules.pop() else: n[0] = sc result = n @@ -493,6 +501,20 @@ proc semGenericStmt(c: PContext, n: PNode, of nkExprColonExpr, nkExprEqExpr: checkMinSonsLen(n, 2, c.config) result[1] = semGenericStmt(c, n[1], flags, ctx) + of nkObjConstr: + for i in 0.. Date: Tue, 21 Mar 2023 15:24:57 +0100 Subject: [PATCH 13/55] atlas tool: 'update' command (#21557) --- tools/atlas/atlas.md | 6 ++++++ tools/atlas/atlas.nim | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/tools/atlas/atlas.md b/tools/atlas/atlas.md index ee770cd626..61ca28ff00 100644 --- a/tools/atlas/atlas.md +++ b/tools/atlas/atlas.md @@ -79,3 +79,9 @@ in its description (or name or list of tags). ### Install Use the .nimble file to setup the project's dependencies. + +### Update [filter] + +Update every package in the workspace that has a remote URL that +matches `filter` if a filter is given. The package is only updated +if there are no uncommitted changes. diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index 274e94517e..dfa60856ef 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -9,9 +9,11 @@ ## Simple tool to automate frequent workflows: Can "clone" ## a Nimble dependency and its dependencies recursively. -import std/[parseopt, strutils, os, osproc, unicode, tables, sets, json, jsonutils] +import std/[parseopt, strutils, os, osproc, tables, sets, json, jsonutils] import parse_requires, osutils, packagesjson +from unicode import nil + const Version = "0.2" Usage = "atlas - Nim Package Cloner Version " & Version & """ @@ -25,6 +27,8 @@ Command: search keyw keywB... search for package that contains the given keywords extract file.nimble extract the requirements and custom commands from the given Nimble file + update [filter] update every package in the workspace that has a remote + URL that matches `filter` if a filter is given Options: --keepCommits do not perform any `git checkouts` @@ -146,7 +150,7 @@ proc toDepRelation(s: string): DepRelation = of ">": strictlyGreater else: normal -proc isCleanGit(c: var AtlasContext; dir: string): string = +proc isCleanGit(c: var AtlasContext): string = result = "" let (outp, status) = exec(c, GitDiff, []) if outp.len != 0: @@ -264,7 +268,7 @@ proc checkoutCommit(c: var AtlasContext; w: Dependency) = if w.commit.len == 0 or cmpIgnoreCase(w.commit, "head") == 0: gitPull(c, w.name) else: - let err = isCleanGit(c, dir) + let err = isCleanGit(c) if err != "": warn c, w.name, err else: @@ -448,6 +452,27 @@ proc installDependencies(c: var AtlasContext; nimbleFile: string) = let paths = cloneLoop(c, work) patchNimCfg(c, paths, if c.cfgHere: getCurrentDir() else: findSrcDir(c)) +proc updateWorkspace(c: var AtlasContext; filter: string) = + for kind, file in walkDir(c.workspace): + if kind == pcDir and dirExists(file / ".git"): + c.withDir file: + let pkg = PackageName(file) + let (remote, _) = osproc.execCmdEx("git remote -v") + if filter.len == 0 or filter in remote: + let diff = isCleanGit(c) + if diff != "": + warn(c, pkg, "has uncommitted changes; skipped") + else: + let (branch, _) = osproc.execCmdEx("git rev-parse --abbrev-ref HEAD") + if branch.strip.len > 0: + let (output, exitCode) = osproc.execCmdEx("git pull origin " & branch.strip) + if exitCode != 0: + error c, pkg, output + else: + message(c, "[Hint] ", pkg, "successfully updated") + else: + error c, pkg, "could not fetch current branch name" + proc main = var action = "" var args: seq[string] = @[] @@ -525,6 +550,8 @@ proc main = of "search", "list": updatePackages(c) search getPackages(c.workspace), args + of "update": + updateWorkspace(c, if args.len == 0: "" else: args[0]) of "extract": singleArg() if fileExists(args[0]): From e8a70ff1794e941a6930c3d240af16a708b59339 Mon Sep 17 00:00:00 2001 From: tersec Date: Wed, 22 Mar 2023 22:05:20 +0100 Subject: [PATCH 14/55] don't access void* out of alignment in refc GC to avoid UB (#21560) --- lib/system/gc_common.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 54c51e7838..eb08845603 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -391,7 +391,6 @@ else: let regEnd = sp +% sizeof(registers) while sp <% regEnd: gcMark(gch, cast[PPointer](sp)[]) - gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[]) sp = sp +% sizeof(pointer) # Make sure sp is word-aligned sp = sp and not (sizeof(pointer) - 1) From 55636a2913d0b0dec6b24568cb6baef43a9220c1 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 23 Mar 2023 23:10:14 +0800 Subject: [PATCH 15/55] fixes #14255; Crash in compiler when using `system.any` by accident. (#21562) fixes #14255; Crash in compiler when using system.any by accident. --- compiler/semexprs.nim | 2 +- tests/arc/t20588.nim | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f74a72692b..d3c96ce3c4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -302,7 +302,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = result = newNodeI(nkConv, n.info) var targetType = semTypeNode(c, n[0], nil) - case targetType.kind + case targetType.skipTypes({tyDistinct}).kind of tyTypeDesc: internalAssert c.config, targetType.len > 0 if targetType.base.kind == tyNone: diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim index d747c656d3..008bd1dcd3 100644 --- a/tests/arc/t20588.nim +++ b/tests/arc/t20588.nim @@ -5,6 +5,7 @@ discard """ t20588.nim(20, 12) Error: illegal type conversion to 'auto' t20588.nim(21, 14) Error: illegal type conversion to 'typed' t20588.nim(22, 16) Error: illegal type conversion to 'untyped' +t20588.nim(24, 7) Error: illegal type conversion to 'any' ''' """ @@ -16,7 +17,9 @@ t20588.nim(22, 16) Error: illegal type conversion to 'untyped' - discard 0.0.auto discard typed("abc") discard untyped(4) +var a = newSeq[bool](1000) +if any(a): + echo "ok?" \ No newline at end of file From 3936071772d648f98c36e5aad16a341b86344e6c Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili Date: Mon, 27 Mar 2023 16:10:51 +0100 Subject: [PATCH 16/55] Add `cursor` to lists iterator variables (#21527) * followup #21507 --- lib/pure/collections/lists.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index 6e3ddf3975..e1d32e7372 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -286,7 +286,7 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it = L.head + var it {.cursor.} = L.head while it != nil: let nxt = it.next yield it @@ -311,7 +311,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it = L.head + var it {.cursor.} = L.head if it != nil: while true: let nxt = it.next @@ -733,7 +733,7 @@ proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.disc if L.tail.next == n: L.tail.next = L.head # restore cycle else: - var prev = L.head + var prev {.cursor.} = L.head while prev.next != n and prev.next != nil: prev = prev.next if prev.next == nil: From 7d83dfd0d1a416cdf36a23b61c87a1ad2c234909 Mon Sep 17 00:00:00 2001 From: heterodoxic <122719743+heterodoxic@users.noreply.github.com> Date: Mon, 27 Mar 2023 17:20:20 +0200 Subject: [PATCH 17/55] fixes #21505 (overload resolution of explicit constructors for imported C++ types) (#21511) hacky attempt to reconcile default explicit constructors with enforcement of brace initialization, instead of memsetting imported objects to 0 --- compiler/ccgtypes.nim | 8 +++++++- compiler/cgen.nim | 21 ++++++++++++++++---- tests/ccgbugs/tbug21505.nim | 39 +++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 tests/ccgbugs/tbug21505.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 98c1fc55fe..6cc009bb96 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -200,6 +200,9 @@ proc isImportedCppType(t: PType): bool = result = (t.sym != nil and sfInfixCall in t.sym.flags) or (x.sym != nil and sfInfixCall in x.sym.flags) +proc isOrHasImportedCppType(typ: PType): bool = + searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType) + proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope proc isObjLackingTypeField(typ: PType): bool {.inline.} = @@ -553,7 +556,10 @@ proc genRecordFieldsAux(m: BModule, n: PNode, else: # don't use fieldType here because we need the # tyGenericInst for C++ template support - result.addf("$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, noAlias]) + if fieldType.isOrHasImportedCppType(): + result.addf("$1$3 $2{};$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, noAlias]) + else: + result.addf("$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, noAlias]) else: internalError(m.config, n.info, "genRecordFieldsAux()") proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope = diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 5ba68580da..cc673d0824 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -487,9 +487,6 @@ proc resetLoc(p: BProc, loc: var TLoc) = # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) -proc isOrHasImportedCppType(typ: PType): bool = - searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType) - proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = let typ = loc.t if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}: @@ -644,7 +641,23 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = if sfVolatile in s.flags: decl.add(" volatile") if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") if value != "": - decl.addf(" $1 = $2;$n", [s.loc.r, value]) + if p.module.compileToCpp and value.startsWith "{{}": + # TODO: taking this branch, re"\{\{\}(,\s\{\})*\}" might be emitted, resulting in + # either warnings (GCC 12.2+) or errors (Clang 15, MSVC 19.3+) of C++11+ compilers **when + # explicit constructors are around** due to overload resolution rules in place [^0][^1][^2] + # *Workaround* here: have C++'s static initialization mechanism do the default init work, + # for us lacking a deeper knowledge of an imported object's constructors' ex-/implicitness + # (so far) *and yet* trying to achieve default initialization. + # Still, generating {}s in genConstObjConstr() just to omit them here is faaaar from ideal; + # need to figure out a better way, possibly by keeping around more data about the + # imported objects' contructors? + # + # [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization + # [^1]: https://cplusplus.github.io/CWG/issues/1518.html + # [^2]: https://eel.is/c++draft/over.match.ctor + decl.addf(" $1;$n", [s.loc.r]) + else: + decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: decl.addf(" $1;$n", [s.loc.r]) else: diff --git a/tests/ccgbugs/tbug21505.nim b/tests/ccgbugs/tbug21505.nim new file mode 100644 index 0000000000..0c0811ec5b --- /dev/null +++ b/tests/ccgbugs/tbug21505.nim @@ -0,0 +1,39 @@ +discard """ + action: "compile" + targets: "cpp" + cmd: "nim cpp $file" +""" + +# see #21505: ensure compilation of imported C++ objects with explicit constructors while retaining default initialization through codegen changes due to #21279 + +{.emit:"""/*TYPESECTION*/ + +struct ExplObj +{ + explicit ExplObj(int bar = 0) {} +}; + +struct BareObj +{ + BareObj() {} +}; + +""".} + +type + ExplObj {.importcpp.} = object + BareObj {.importcpp.} = object + +type + Composer = object + explObj: ExplObj + bareObj: BareObj + +proc foo = + var composer1 {.used.}: Composer + let composer2 {.used.} = Composer() + +var composer1 {.used.}: Composer +let composer2 {.used.} = Composer() + +foo() \ No newline at end of file From ff5ed1dbb11ff44217ecd8353dc94a6929fadbd0 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 28 Mar 2023 14:29:12 +0800 Subject: [PATCH 18/55] Revert "Add `cursor` to lists iterator variables" (#21571) Revert "Add `cursor` to lists iterator variables (#21527)" This reverts commit 3936071772d648f98c36e5aad16a341b86344e6c. --- lib/pure/collections/lists.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index e1d32e7372..6e3ddf3975 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -286,7 +286,7 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it {.cursor.} = L.head + var it = L.head while it != nil: let nxt = it.next yield it @@ -311,7 +311,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it {.cursor.} = L.head + var it = L.head if it != nil: while true: let nxt = it.next @@ -733,7 +733,7 @@ proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.disc if L.tail.next == n: L.tail.next = L.head # restore cycle else: - var prev {.cursor.} = L.head + var prev = L.head while prev.next != n and prev.next != nil: prev = prev.next if prev.next == nil: From 0630c649c6b4fca4abfa157c5bc6f9f9e50e9c65 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 28 Mar 2023 11:34:30 +0300 Subject: [PATCH 19/55] disable google request in thttpclient (#21572) was breaking macos CI --- tests/stdlib/thttpclient.nim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index c731c81b0c..00e728fa2d 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -57,8 +57,9 @@ proc asyncTest() {.async.} = doAssert(resp.code == Http404) doAssert(resp.status == $Http404) - resp = await client.request("https://google.com/") - doAssert(resp.code.is2xx or resp.code.is3xx) + when false: # occasionally does not give success code + resp = await client.request("https://google.com/") + doAssert(resp.code.is2xx or resp.code.is3xx) # getContent try: @@ -118,8 +119,9 @@ proc syncTest() = doAssert(resp.code == Http404) doAssert(resp.status == $Http404) - resp = client.request("https://google.com/") - doAssert(resp.code.is2xx or resp.code.is3xx) + when false: # occasionally does not give success code + resp = client.request("https://google.com/") + doAssert(resp.code.is2xx or resp.code.is3xx) # getContent try: From 115cec17452dc13aed7a1300f7786177f886e9c7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 28 Mar 2023 13:27:17 +0200 Subject: [PATCH 20/55] fixes #20993 [backport:1.6] (#21574) * fixes #20993 [backport:1.6] * proper line endings for the test file --- compiler/injectdestructors.nim | 49 ++++++++++++++++++++++----------- tests/arc/taliased_reassign.nim | 41 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 tests/arc/taliased_reassign.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 5ddd49621f..96102d54d2 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -249,7 +249,17 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} = proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags -proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = +proc deepAliases(dest, ri: PNode): bool = + case ri.kind + of nkCallKinds, nkStmtListExpr, nkBracket, nkTupleConstr, nkObjConstr, + nkCast, nkConv, nkObjUpConv, nkObjDownConv: + for r in ri: + if deepAliases(dest, r): return true + return false + else: + return aliases(dest, ri) != no + +proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or isNoInit(dest): @@ -263,7 +273,14 @@ proc genSink(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNod else: # the default is to use combination of `=destroy(dest)` and # and copyMem(dest, source). This is efficient. - result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri)) + if deepAliases(dest, ri): + # consider: x = x + y, it is wrong to destroy the destination first! + # tmp to support self assignments + let tmp = c.getTemp(s, dest.typ, dest.info) + result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri), + c.genDestroy(tmp)) + else: + result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri)) proc isCriticalLink(dest: PNode): bool {.inline.} = #[ @@ -454,7 +471,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = # This was already done in the sink parameter handling logic. result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) let tmp = c.getTemp(s, arg.typ, arg.info) - result.add c.genSink(tmp, arg, {IsDecl}) + result.add c.genSink(s, tmp, arg, {IsDecl}) result.add tmp s.final.add c.genDestroy(tmp) else: @@ -1004,7 +1021,7 @@ proc sameLocation*(a, b: PNode): bool = of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b) else: false -proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = +proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = # with side effects var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info) temp.typ = ri[1].typ @@ -1021,7 +1038,7 @@ proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode; flags: set[MoveOrCop newAccess.add ri[0] newAccess.add tempAsNode - var snk = c.genSink(dest, newAccess, flags) + var snk = c.genSink(s, dest, newAccess, flags) result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess)) proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode = @@ -1039,21 +1056,21 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy else: case ri.kind of nkCallKinds: - result = c.genSink(dest, p(ri, c, s, consumed), flags) + result = c.genSink(s, dest, p(ri, c, s, consumed), flags) of nkBracketExpr: if isUnpackedTuple(ri[0]): # unpacking of tuple: take over the elements - result = c.genSink(dest, p(ri, c, s, consumed), flags) + result = c.genSink(s, dest, p(ri, c, s, consumed), flags) elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s): if aliases(dest, ri) == no: # Rule 3: `=sink`(x, z); wasMoved(z) if isAtom(ri[1]): - var snk = c.genSink(dest, ri, flags) + var snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = genFieldAccessSideEffects(c, dest, ri, flags) + result = genFieldAccessSideEffects(c, s, dest, ri, flags) else: - result = c.genSink(dest, destructiveMoveVar(ri, c, s), flags) + result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags) else: result = c.genCopy(dest, ri, flags) result.add p(ri, c, s, consumed) @@ -1065,25 +1082,25 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) else: - result = c.genSink(dest, p(ri, c, s, consumed), flags) + result = c.genSink(s, dest, p(ri, c, s, consumed), flags) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = c.genSink(dest, p(ri, c, s, consumed), flags) + result = c.genSink(s, dest, p(ri, c, s, consumed), flags) of nkSym: if isSinkParam(ri.sym) and isLastRead(ri, c, s): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, flags) + let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) elif ri.sym.kind != skParam and ri.sym.owner == c.owner and isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, flags) + let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: result = c.genCopy(dest, ri, flags) result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: - result = c.genSink(dest, p(ri, c, s, sinkArg), flags) + result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags) # We know the result will be a stmt so we use that fact to optimize @@ -1094,7 +1111,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, flags) + let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: result = c.genCopy(dest, ri, flags) diff --git a/tests/arc/taliased_reassign.nim b/tests/arc/taliased_reassign.nim new file mode 100644 index 0000000000..5563fae8c4 --- /dev/null +++ b/tests/arc/taliased_reassign.nim @@ -0,0 +1,41 @@ +discard """ + matrix: "--mm:orc" +""" + +# bug #20993 + +type + Dual[int] = object # must be generic (even if fully specified) + p: int +proc D(p: int): Dual[int] = Dual[int](p: p) +proc `+`(x: Dual[int], y: Dual[int]): Dual[int] = D(x.p + y.p) + +type + Tensor[T] = object + buf: seq[T] +proc newTensor*[T](s: int): Tensor[T] = Tensor[T](buf: newSeq[T](s)) +proc `[]`*[T](t: Tensor[T], idx: int): T = t.buf[idx] +proc `[]=`*[T](t: var Tensor[T], idx: int, val: T) = t.buf[idx] = val + +proc `+.`[T](t1, t2: Tensor[T]): Tensor[T] = + let n = t1.buf.len + result = newTensor[T](n) + for i in 0 ..< n: + result[i] = t1[i] + t2[i] + +proc toTensor*[T](a: sink seq[T]): Tensor[T] = + ## This breaks it: Using `T` instead makes it work + type U = typeof(a[0]) + var t: Tensor[U] # Tensor[T] works + t.buf = a + result = t + +proc loss() = + var B = toTensor(@[D(123)]) + let a = toTensor(@[D(-10)]) + B = B +. a + doAssert B[0].p == 113, "I want to be 113, but I am " & $B[0].p + +loss() + + From 4fc9f0c3a3c6f53bfb663e60775e3d7a75c56337 Mon Sep 17 00:00:00 2001 From: Zoom Date: Tue, 28 Mar 2023 14:37:49 +0300 Subject: [PATCH 21/55] Docs: Mention Source Code Filters in `lib/String handling` (#21570) Mention Source Code Filters in `String handling` ...as a viable solution for templating --- doc/lib.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/lib.md b/doc/lib.md index d6ec408ec7..bc78027a02 100644 --- a/doc/lib.md +++ b/doc/lib.md @@ -171,7 +171,9 @@ String handling * [strformat](strformat.html) Macro based standard string interpolation/formatting. Inspired by - Python's f-strings. + Python's f-strings.\ + **Note:** if you need templating, consider using Nim + [Source Code Filters (SCF)](filters.html). * [strmisc](strmisc.html) This module contains uncommon string handling operations that do not From 2315b01ae691e5e9e54fdfdfb4642c8fbc559e48 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 28 Mar 2023 18:52:23 +0300 Subject: [PATCH 22/55] tuple unpacking for vars as just sugar, allowing nesting (#21563) * tuple unpacking for vars as just sugar, allowing nesting * set temp symbol AST * hopeful fix some issues, add test for #19364 * always use temp for consts * document, fix small issue * fix manual indentation * actually fix manual * use helper proc * don't resem temp tuple assignment --- changelogs/changelog_2_0_0.md | 20 ++++ compiler/parser.nim | 12 +- compiler/pipelines.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semstmts.nim | 193 +++++++++++++++++------------- doc/grammar.txt | 3 +- doc/manual.md | 33 ++++- tests/arc/t19364.nim | 30 +++++ tests/macros/tastrepr.nim | 4 +- tests/misc/t11634.nim | 3 - tests/parser/ttupleunpack.nim | 48 ++++++++ tests/tuples/mnimsconstunpack.nim | 4 + tests/tuples/tnimsconstunpack.nim | 8 ++ 13 files changed, 267 insertions(+), 95 deletions(-) create mode 100644 tests/arc/t19364.nim create mode 100644 tests/tuples/mnimsconstunpack.nim create mode 100644 tests/tuples/tnimsconstunpack.nim diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index cbe7554785..19d97b7690 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -343,6 +343,26 @@ - `=wasMoved` can be overridden by users. +- Tuple unpacking for variables is now treated as syntax sugar that directly + expands into multiple assignments. Along with this, tuple unpacking for + variables can now be nested. + + ```nim + proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3) + + let (x, (_, y), _, z) = returnsNestedTuple() + # roughly becomes + let + tmpTup1 = returnsNestedTuple() + x = tmpTup1[0] + tmpTup2 = tmpTup1[1] + y = tmpTup2[1] + z = tmpTup1[3] + ``` + + As a result `nnkVarTuple` nodes in variable sections will no longer be + reflected in `typed` AST. + ## Compiler changes - The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the diff --git a/compiler/parser.nim b/compiler/parser.nim index abfc7b3239..c9b30204c1 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2277,13 +2277,19 @@ proc parseTypeDef(p: var Parser): PNode = setEndInfo() proc parseVarTuple(p: var Parser): PNode = - #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr + #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' + #| varTuple = varTupleLhs '=' optInd expr result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' optInd(p, result) # progress guaranteed - while p.tok.tokType in {tkSymbol, tkAccent}: - var a = identWithPragma(p, allowDot=true) + while p.tok.tokType in {tkSymbol, tkAccent, tkParLe}: + var a: PNode + if p.tok.tokType == tkParLe: + a = parseVarTuple(p) + a.add(p.emptyNode) + else: + a = identWithPragma(p, allowDot=true) result.add(a) if p.tok.tokType != tkComma: break getTok(p) diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index 2bb0bc2473..90f6de5c09 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -1,6 +1,6 @@ import sem, cgen, modulegraphs, ast, llstream, parser, msgs, lineinfos, reorder, options, semdata, cgendata, modules, pathutils, - packages, syntaxes, depends, vm, pragmas, idents, lookups + packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg import pipelineutils diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d3c96ce3c4..7ba4099d3e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1824,7 +1824,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result.add(n[1]) return semExprNoType(c, result) of nkPar, nkTupleConstr: - if a.len >= 2: + if a.len >= 2 or a.kind == nkTupleConstr: # unfortunately we need to rewrite ``(x, y) = foo()`` already here so # that overloading of the assignment operator still works. Usually we # prefer to do these rewritings in transf.nim: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6d1ce388dd..2aa953d938 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -583,6 +583,57 @@ proc globalVarInitCheck(c: PContext, n: PNode) = if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n): localError(c.config, n.info, errCannotAssignToGlobal) +proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode = + ## expand tuple unpacking assignments into new var/let/const section + if typ.kind != tyTuple: + localError(c.config, a.info, errXExpected, "tuple") + elif a.len-2 != typ.len: + localError(c.config, a.info, errWrongNumberOfVariables) + var + tmpTuple: PSym + lastDef: PNode + let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs + # temporary not needed if not const and RHS is tuple literal + # const breaks with seqs without temporary + let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst + if useTemp: + # use same symkind for compatibility with original section + tmpTuple = newSym(symkind, getIdent(c.cache, "tmpTuple"), nextSymId c.idgen, getCurrOwner(c), n.info) + tmpTuple.typ = typ + tmpTuple.flags.incl(sfGenSym) + lastDef = newNodeI(defkind, a.info) + newSons(lastDef, 3) + lastDef[0] = newSymNode(tmpTuple) + # NOTE: at the moment this is always ast.emptyNode, see parser.nim + lastDef[1] = a[^2] + lastDef[2] = def + tmpTuple.ast = lastDef + addToVarSection(c, origResult, n, lastDef) + result = newNodeI(n.kind, a.info) + for j in 0.. 3: - message(c.config, a.info, warnEachIdentIsTuple) + # generate new section from tuple unpacking and embed it into this one + let assignments = makeVarTupleSection(c, n, a, def, tup, symkind, result) + let resSection = semVarOrLet(c, assignments, symkind) + for resDef in resSection: + addToVarSection(c, result, n, resDef) + else: + if tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and + a.len > 3: + # var a, b = (1, 2) + message(c.config, a.info, warnEachIdentIsTuple) - for j in 0.. 0: v.flags.incl(sfShadowed) + for j in 0.. 0: v.flags.incl(sfShadowed) + else: + let shadowed = findShadowedVar(c, v) + if shadowed != nil: + shadowed.flags.incl(sfShadowed) + if shadowed.kind == skResult and sfGenSym notin v.flags: + message(c.config, a.info, warnResultShadowed) if def.kind != nkEmpty: if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit) setVarType(c, v, typ) @@ -708,35 +753,26 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.add copyTree(def) addToVarSection(c, result, n, b) v.ast = b - else: - if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] - # bug #7663, for 'nim check' this can be a non-tuple: - if tup.kind == tyTuple: setVarType(c, v, tup[j]) - else: v.typ = tup - b[j] = newSymNode(v) - if def.kind == nkEmpty: - let actualType = v.typ.skipTypes({tyGenericInst, tyAlias, - tyUserTypeClassInst}) - if actualType.kind in {tyObject, tyDistinct} and - actualType.requiresInit: - defaultConstructionError(c, v.typ, v.info) - else: - checkNilable(c, v) - # allow let to not be initialised if imported from C: - if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)): - localError(c.config, a.info, errLetNeedsInit) - if sfCompileTime in v.flags: - if a.kind != nkVarTuple: + if def.kind == nkEmpty: + let actualType = v.typ.skipTypes({tyGenericInst, tyAlias, + tyUserTypeClassInst}) + if actualType.kind in {tyObject, tyDistinct} and + actualType.requiresInit: + defaultConstructionError(c, v.typ, v.info) + else: + checkNilable(c, v) + # allow let to not be initialised if imported from C: + if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)): + localError(c.config, a.info, errLetNeedsInit) + if sfCompileTime in v.flags: var x = newNodeI(result.kind, v.info) x.add result[i] vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x) - else: - localError(c.config, a.info, "cannot destructure to compile time variable") - if v.flags * {sfGlobal, sfThread} == {sfGlobal}: - message(c.config, v.info, hintGlobalVar) - if {sfGlobal, sfPure} <= v.flags: - globalVarInitCheck(c, def) - suggestSym(c.graph, v.info, v, c.graph.usageSym) + if v.flags * {sfGlobal, sfThread} == {sfGlobal}: + message(c.config, v.info, hintGlobalVar) + if {sfGlobal, sfPure} <= v.flags: + globalVarInitCheck(c, def) + suggestSym(c.graph, v.info, v, c.graph.usageSym) proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) @@ -789,23 +825,19 @@ proc semConst(c: PContext, n: PNode): PNode = typeAllowedCheck(c, a.info, typ, skConst, typFlags) if a.kind == nkVarTuple: - if typ.kind != tyTuple: - localError(c.config, a.info, errXExpected, "tuple") - elif a.len-2 != typ.len: - localError(c.config, a.info, errWrongNumberOfVariables) - b = newNodeI(nkVarTuple, a.info) - newSons(b, a.len) - b[^2] = a[^2] - b[^1] = def + # generate new section from tuple unpacking and embed it into this one + let assignments = makeVarTupleSection(c, n, a, def, typ, skConst, result) + let resSection = semConst(c, assignments) + for resDef in resSection: + addToVarSection(c, result, n, resDef) + else: + for j in 0..} stmt typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue indAndComment? -varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr +varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' +varTuple = varTupleLhs '=' optInd expr colonBody = colcom stmt postExprBlocks? variable = (varTuple / identColonEquals) colonBody? indAndComment constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment diff --git a/doc/manual.md b/doc/manual.md index 98f9d2ec3e..1fd35e7b3b 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -3080,8 +3080,8 @@ value is expected to come from native code, typically a C/C++ `const`. Tuple unpacking --------------- -In a `var` or `let` statement tuple unpacking can be performed. The special -identifier `_` can be used to ignore some parts of the tuple: +In a `var`, `let` or `const` statement tuple unpacking can be performed. +The special identifier `_` can be used to ignore some parts of the tuple: ```nim proc returnsTuple(): (int, int, int) = (4, 2, 3) @@ -3089,6 +3089,35 @@ identifier `_` can be used to ignore some parts of the tuple: let (x, _, z) = returnsTuple() ``` +This is treated as syntax sugar for roughly the following: + + ```nim + let + tmpTuple = returnsTuple() + x = tmpTuple[0] + z = tmpTuple[2] + ``` + +For `var` or `let` statements, if the value expression is a tuple literal, +each expression is directly expanded into an assignment without the use of +a temporary variable. + + ```nim + let (x, y, z) = (1, 2, 3) + # becomes + let + x = 1 + y = 2 + z = 3 + ``` + +Tuple unpacking can also be nested: + + ```nim + proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3) + + let (x, (_, y), _, z) = returnsNestedTuple() + ``` Const section diff --git a/tests/arc/t19364.nim b/tests/arc/t19364.nim new file mode 100644 index 0000000000..f520f32916 --- /dev/null +++ b/tests/arc/t19364.nim @@ -0,0 +1,30 @@ +discard """ + cmd: '''nim c --gc:arc --expandArc:fooLeaks $file''' + nimout: ''' +--expandArc: fooLeaks + +var + tmpTuple_cursor + a_cursor + b_cursor + c_cursor +tmpTuple_cursor = refTuple +a_cursor = tmpTuple_cursor[0] +b_cursor = tmpTuple_cursor[1] +c_cursor = tmpTuple_cursor[2] +-- end of expandArc ------------------------ +''' +""" + +func fooLeaks(refTuple: tuple[a, + b, + c: seq[float]]): float = + let (a, b, c) = refTuple + +let refset = (a: newSeq[float](25_000_000), + b: newSeq[float](25_000_000), + c: newSeq[float](25_000_000)) + +var res = newSeq[float](1_000_000) +for i in 0 .. res.high: + res[i] = fooLeaks(refset) diff --git a/tests/macros/tastrepr.nim b/tests/macros/tastrepr.nim index 759ca55b5e..c04498a25b 100644 --- a/tests/macros/tastrepr.nim +++ b/tests/macros/tastrepr.nim @@ -8,7 +8,9 @@ for i, d in pairs(data): discard for i, (x, y) in pairs(data): discard -var (a, b) = (1, 2) +var + a = 1 + b = 2 var data = @[(1, "one"), (2, "two")] for (i, d) in pairs(data): diff --git a/tests/misc/t11634.nim b/tests/misc/t11634.nim index 4ecb6a53c5..390af40f4f 100644 --- a/tests/misc/t11634.nim +++ b/tests/misc/t11634.nim @@ -1,8 +1,5 @@ discard """ action: reject - nimout: ''' -t11634.nim(20, 7) Error: cannot destructure to compile time variable -''' """ type Foo = ref object diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim index c7ab9ea153..860ef66cff 100644 --- a/tests/parser/ttupleunpack.nim +++ b/tests/parser/ttupleunpack.nim @@ -27,3 +27,51 @@ proc main() = main() main2() + +block: # nested unpacking + block: # simple let + let (a, (b, c), d) = (1, (2, 3), 4) + doAssert (a, b, c, d) == (1, 2, 3, 4) + let foo = (a, (b, c), d) + let (a2, (b2, c2), d2) = foo + doAssert (a, b, c, d) == (a2, b2, c2, d2) + + block: # var and assignment + var (x, (y, z), t) = ('a', (true, @[123]), "abc") + doAssert (x, y, z, t) == ('a', true, @[123], "abc") + (x, (y, z), t) = ('b', (false, @[456]), "def") + doAssert (x, y, z, t) == ('b', false, @[456], "def") + + block: # very nested + let (_, (_, (_, (_, (_, a))))) = (1, (2, (3, (4, (5, 6))))) + doAssert a == 6 + + block: # const + const (a, (b, c), d) = (1, (2, 3), 4) + doAssert (a, b, c, d) == (1, 2, 3, 4) + const foo = (a, (b, c), d) + const (a2, (b2, c2), d2) = foo + doAssert (a, b, c, d) == (a2, b2, c2, d2) + + block: # evaluation semantics preserved between literal and not literal + var s: seq[string] + block: # literal + let (a, (b, c), d) = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4)) + doAssert (a, b, c, d) == (1, 2, 3, 4) + doAssert s == @["a", "b", "c", "d"] + block: # underscore + s = @[] + let (a, (_, c), _) = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4)) + doAssert (a, c) == (1, 3) + doAssert s == @["a", "b", "c", "d"] + block: # temp + s = @[] + let foo = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4)) + let (a, (b, c), d) = foo + doAssert (a, b, c, d) == (1, 2, 3, 4) + doAssert s == @["a", "b", "c", "d"] + +block: # unary assignment unpacking + var a: int + (a,) = (1,) + doAssert a == 1 diff --git a/tests/tuples/mnimsconstunpack.nim b/tests/tuples/mnimsconstunpack.nim new file mode 100644 index 0000000000..65fafc12f8 --- /dev/null +++ b/tests/tuples/mnimsconstunpack.nim @@ -0,0 +1,4 @@ +proc foo(): tuple[a, b: string] = + result = ("a", "b") + +const (a, b*) = foo() diff --git a/tests/tuples/tnimsconstunpack.nim b/tests/tuples/tnimsconstunpack.nim new file mode 100644 index 0000000000..7860fc0a45 --- /dev/null +++ b/tests/tuples/tnimsconstunpack.nim @@ -0,0 +1,8 @@ +discard """ + action: compile + cmd: "nim e $file" +""" + +import mnimsconstunpack + +doAssert b == "b" From c06623bf8ccfccf4788e9f4d2f044ab1bde6fe46 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Tue, 28 Mar 2023 20:50:56 -0600 Subject: [PATCH 23/55] Fix segfault caused by ensuring valueless statics are not evaluated (#21577) --- compiler/semtypinst.nim | 2 +- tests/statictypes/tstatictypes.nim | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0ef1d0898f..2fe8c55e41 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -204,7 +204,7 @@ proc hasValuelessStatics(n: PNode): bool = proc doThing(_: MyThing) ]# if n.safeLen == 0: - n.typ.kind == tyStatic + n.typ == nil or n.typ.kind == tyStatic else: for x in n: if hasValuelessStatics(x): diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 5df3f35fd5..9b2d81b258 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -391,3 +391,23 @@ var sorted = newSeq[int](1000) for i in 0.. Date: Wed, 29 Mar 2023 10:00:00 +0000 Subject: [PATCH 24/55] remove `seq[T]` `setLen` undefined behavior (#21582) remove seq[T] setLen UB --- compiler/ccgexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a64a42219c..80f9fb563b 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1947,7 +1947,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = initLoc(call, locCall, e, OnHeap) if not p.module.compileToCpp: - const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)" + const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)" call.r = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) From ecf9efa3977f95aed5229ab79cd6ac4799a32a4c Mon Sep 17 00:00:00 2001 From: metagn Date: Thu, 30 Mar 2023 16:34:42 +0300 Subject: [PATCH 25/55] document general use of `_`, error message, fixes (#21584) * document general use of `_`, error message, fixes fixes #20687, fixes #21435 Documentation and changelog updated to clarify new universal behavior of `_`. Also new error message for attempting to use `_`, new tests, and fixes with overloadable symbols and implicit generics. * add test for #21435 --- changelogs/changelog_2_0_0.md | 36 +++++++++++++------ compiler/lookups.nim | 27 ++++++++------ compiler/semtypes.nim | 13 ++++--- doc/manual.md | 13 +++++++ tests/proc/tunderscoreparam.nim | 13 +++++++ .../tforloop_tuple_multiple_underscore.nim | 4 +-- tests/stmt/tforloop_tuple_underscore.nim | 2 +- tests/stmt/tforloop_underscore.nim | 2 +- tests/stmt/tgenericsunderscore.nim | 4 +++ tests/stmt/tmiscunderscore.nim | 15 ++++++++ tests/template/tunderscore1.nim | 2 +- 11 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 tests/stmt/tgenericsunderscore.nim create mode 100644 tests/stmt/tmiscunderscore.nim diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index 19d97b7690..a82ce149bf 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -135,26 +135,42 @@ - The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. -- Underscores (`_`) as routine parameters are now ignored and cannot be used in the routine body. - The following code now does not compile: +- The underscore identifier (`_`) is now generally not added to scope when + used as the name of a definition. While this was already the case for + variables, it is now also the case for routine parameters, generic + parameters, routine declarations, type declarations, etc. This means that the following code now does not compile: ```nim proc foo(_: int): int = _ + 1 echo foo(1) + + proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] + echo foo[int]() + + proc _() = echo "_" + _() + + type _ = int + let x: _ = 3 ``` - Instead, the following code now compiles: + Whereas the following code now compiles: ```nim proc foo(_, _: int): int = 123 echo foo(1, 2) - ``` -- Underscores (`_`) as generic parameters are not supported and cannot be used. - Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else: - - ```nim - proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile - proc foo[T](t: typedesc[T]): string = "BAR" # Can compile + + proc foo[_, _](): int = 123 + echo foo[int, bool]() + + proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U)) + echo foo(int, bool) + + proc _() = echo "one" + proc _() = echo "two" + + type _ = int + type _ = float ``` - - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 3f028a52f1..fc84b90513 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -396,11 +396,12 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = if fn.kind notin OverloadableSyms: internalError(c.config, fn.info, "addOverloadableSymAt") return - let check = strTableGet(scope.symbols, fn.name) - if check != nil and check.kind notin OverloadableSyms: - wrongRedefinition(c, fn.info, fn.name.s, check.info) - else: - scope.addSym(fn) + if fn.name.s != "_": + let check = strTableGet(scope.symbols, fn.name) + if check != nil and check.kind notin OverloadableSyms: + wrongRedefinition(c, fn.info, fn.name.s, check.info) + else: + scope.addSym(fn) proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = ## adds an overloadable symbol on the scope and the interface if appropriate @@ -546,12 +547,16 @@ proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) = errorUseQualifier(c, info, candidates, prefix) proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") = - var err = "undeclared identifier: '" & name & "'" & extra - if c.recursiveDep.len > 0: - err.add "\nThis might be caused by a recursive module dependency:\n" - err.add c.recursiveDep - # prevent excessive errors for 'nim check' - c.recursiveDep = "" + var err: string + if name == "_": + err = "the special identifier '_' is ignored in declarations and cannot be used" + else: + err = "undeclared identifier: '" & name & "'" & extra + if c.recursiveDep.len > 0: + err.add "\nThis might be caused by a recursive module dependency:\n" + err.add c.recursiveDep + # prevent excessive errors for 'nim check' + c.recursiveDep = "" localError(c.config, info, errGenerated, err) proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2f485d65bb..fbba4f36f2 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1359,6 +1359,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, for j in 0.. Date: Thu, 30 Mar 2023 15:35:00 +0200 Subject: [PATCH 26/55] hopefully easier to understand error message (#21585) --- compiler/semcall.nim | 4 ++-- tests/errmsgs/tconcisetypemismatch.nim | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 987fd4a13b..8445b25e71 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -132,7 +132,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, # just in case, should be impossible though if syms.len == 0: break - + if nextSymIndex > high(syms): # we have reached the end break @@ -293,7 +293,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): const errTypeMismatch = "type mismatch: got <" errButExpected = "but expected one of:" - errExpectedPosition = "Expected one of (first mismatch at position [#]):" + errExpectedPosition = "Expected one of (first mismatch at [position]):" errUndeclaredField = "undeclared field: '$1'" errUndeclaredRoutine = "attempting to call undeclared routine: '$1'" errBadRoutine = "attempting to call routine: '$1'$2" diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim index 4227644ceb..c2896604f5 100644 --- a/tests/errmsgs/tconcisetypemismatch.nim +++ b/tests/errmsgs/tconcisetypemismatch.nim @@ -7,7 +7,7 @@ Expression: int(inNanoseconds(t2 - t1)) / 100.5 [1] int(inNanoseconds(t2 - t1)): int [2] 100.5: float64 -Expected one of (first mismatch at position [#]): +Expected one of (first mismatch at [position]): [1] proc `/`(x, y: float): float [1] proc `/`(x, y: float32): float32 [2] proc `/`(x, y: int): float @@ -20,4 +20,4 @@ from times import inNanoseconds let t1 = getMonotime() let result = 1 + 2 let t2 = getMonotime() -echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5 \ No newline at end of file +echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5 From b865f6a5f06253b5e479635ab90f6419f86677d7 Mon Sep 17 00:00:00 2001 From: Yardanico Date: Thu, 30 Mar 2023 16:35:17 +0300 Subject: [PATCH 27/55] Remove the "This module" suffix and reword some entries from the stdlib overview (#21580) Remove a lot of "This module x" and reword some entries --- doc/lib.md | 204 +++++++++++++++++++++++++---------------------------- 1 file changed, 97 insertions(+), 107 deletions(-) diff --git a/doc/lib.md b/doc/lib.md index bc78027a02..a76b29151f 100644 --- a/doc/lib.md +++ b/doc/lib.md @@ -47,17 +47,17 @@ Core Provides a series of low-level methods for bit manipulation. * [compilesettings](compilesettings.html) - This module allows querying the compiler about diverse configuration settings. + Querying the compiler about diverse configuration settings from code. * [cpuinfo](cpuinfo.html) - This module implements procs to determine the number of CPUs / cores. + Procs to determine the number of CPUs / cores. * [effecttraits](effecttraits.html) - This module provides access to the inferred .raises effects + Access to the inferred .raises effects for Nim's macro system. * [endians](endians.html) - This module contains helpers that deal with different byte orders. + Helpers that deal with different byte orders. * [locks](locks.html) Locks and condition variables for Nim. @@ -75,10 +75,10 @@ Core Provides (unsafe) access to Nim's run-time type information. * [typetraits](typetraits.html) - This module defines compile-time reflection procs for working with types. + Compile-time reflection procs for working with types. * [volatile](volatile.html) - This module contains code for generating volatile loads and stores, + Code for generating volatile loads and stores, which are useful in embedded and systems programming. @@ -86,24 +86,24 @@ Algorithms ---------- * [algorithm](algorithm.html) - This module implements some common generic algorithms like sort or binary search. + Some common generic algorithms like sort or binary search. * [enumutils](enumutils.html) - This module adds functionality for the built-in `enum` type. + Additional functionality for the built-in `enum` type. * [sequtils](sequtils.html) - This module implements operations for the built-in `seq` type + Operations for the built-in `seq` type which were inspired by functional programming languages. * [setutils](setutils.html) - This module adds functionality for the built-in `set` type. + Additional functionality for the built-in `set` type. Collections ----------- * [critbits](critbits.html) - This module implements a *crit bit tree* which is an efficient + A *crit bit tree* which is an efficient container for a sorted set of strings, or a sorted mapping of strings. * [deques](deques.html) @@ -127,7 +127,7 @@ Collections Efficient implementation of a set of ordinals as a sparse bit set. * [ropes](ropes.html) - This module contains support for a *rope* data type. + A *rope* data type. Ropes can represent very long strings efficiently; in particular, concatenation is done in O(1) instead of O(n). @@ -150,7 +150,7 @@ String handling Utilities for `cstring` handling. * [editdistance](editdistance.html) - This module contains an algorithm to compute the edit distance between two + An algorithm to compute the edit distance between two Unicode strings. * [encodings](encodings.html) @@ -158,16 +158,16 @@ String handling the `iconv` library, on Windows the Windows API. * [formatfloat](formatfloat.html) - This module implements formatting floats as strings. + Formatting floats as strings. * [objectdollar](objectdollar.html) - This module implements a generic `$` operator to convert objects to strings. + A generic `$` operator to convert objects to strings. * [punycode](punycode.html) Implements a representation of Unicode with the limited ASCII character subset. * [strbasics](strbasics.html) - This module provides some high performance string operations. + Some high performance string operations. * [strformat](strformat.html) Macro based standard string interpolation/formatting. Inspired by @@ -176,19 +176,19 @@ String handling [Source Code Filters (SCF)](filters.html). * [strmisc](strmisc.html) - This module contains uncommon string handling operations that do not - fit with the commonly used operations in strutils. + Uncommon string handling operations that do not + fit with the commonly used operations in [strutils](strutils.html). * [strscans](strscans.html) - This module contains a `scanf` macro for convenient parsing of mini languages. + A `scanf` macro for convenient parsing of mini languages. * [strutils](strutils.html) - This module contains common string handling operations like changing + Common string handling operations like changing case of a string, splitting a string into substrings, searching for substrings, replacing substrings. * [unicode](unicode.html) - This module provides support to handle the Unicode UTF-8 encoding. + Support for handling the Unicode UTF-8 encoding. * [unidecode](unidecode.html) It provides a single proc that does Unicode to ASCII transliterations. @@ -198,7 +198,7 @@ String handling Nim support for C/C++'s wide strings. * [wordwrap](wordwrap.html) - This module contains an algorithm to wordwrap a Unicode string. + An algorithm for word-wrapping Unicode strings. Time handling @@ -215,17 +215,16 @@ Generic Operating System Services --------------------------------- * [appdirs](appdirs.html) - This module implements helpers for determining special directories used by apps. + Helpers for determining special directories used by apps. * [cmdline](cmdline.html) - This module contains system facilities for reading command - line parameters. + System facilities for reading command line parameters. * [dirs](dirs.html) - This module implements directory handling. + Directory handling. * [distros](distros.html) - This module implements the basics for OS distribution ("distro") detection + Basics for OS distribution ("distro") detection and the OS's native package manager. Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum @@ -233,19 +232,19 @@ Generic Operating System Services See [packaging](packaging.html) for hints on distributing Nim using OS packages. * [dynlib](dynlib.html) - This module implements the ability to access symbols from shared libraries. + Accessing symbols from shared libraries. * [envvars](envvars.html) - This module implements environment variable handling. + Environment variable handling. * [exitprocs](exitprocs.html) - This module allows adding hooks to program exit. + Adding hooks to program exit. * [files](files.html) - This module implements file handling. + File handling. * [memfiles](memfiles.html) - This module provides support for memory-mapped files (Posix's `mmap`) + Support for memory-mapped files (Posix's `mmap`) on the different operating systems. * [os](os.html) @@ -254,52 +253,50 @@ Generic Operating System Services commands, etc. * [oserrors](oserrors.html) - This module implements OS error reporting. + OS error reporting. * [osproc](osproc.html) Module for process communication beyond `os.execShellCmd`. * [paths](paths.html) - This module implements path handling. + Path handling. * [reservedmem](reservedmem.html) - This module provides utilities for reserving portions of the + Utilities for reserving portions of the address space of a program without consuming physical memory. * [streams](streams.html) - This module provides a stream interface and two implementations thereof: + 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 may provide other implementations for this standard stream interface. * [symlinks](symlinks.html) - This module implements symlink handling. + Symlink handling. * [syncio](syncio.html) - This module implements various synchronized I/O operations. + Various synchronized I/O operations. * [terminal](terminal.html) - This module contains a few procedures to control the *terminal* - (also called *console*). The implementation simply uses ANSI escape - sequences and does not depend on any other module. + A module to control the terminal output (also called *console*). * [tempfiles](tempfiles.html) - This module provides some utils to generate temporary path names and - create temporary files and directories. + Some utilities for generating temporary path names and + creating temporary files and directories. Math libraries -------------- * [complex](complex.html) - This module implements complex numbers and relevant mathematical operations. + Complex numbers and relevant mathematical operations. * [fenv](fenv.html) Floating-point environment. Handling of floating-point rounding and exceptions (overflow, zero-divide, etc.). * [lenientops](lenientops.html) - Provides binary operators for mixed integer/float expressions for convenience. + Binary operators for mixed integer/float expressions for convenience. * [math](math.html) Mathematical operations like cosine, square root. @@ -308,7 +305,7 @@ Math libraries Fast and tiny random number generator. * [rationals](rationals.html) - This module implements rational numbers and relevant mathematical operations. + Rational numbers and relevant mathematical operations. * [stats](stats.html) Statistical analysis. @@ -324,75 +321,69 @@ Internet Protocols and Support Exports `asyncmacro` and `asyncfutures` for native backends, and `asyncjs` on the JS backend. * [asyncdispatch](asyncdispatch.html) - This module implements an asynchronous dispatcher for IO operations. + An asynchronous dispatcher for IO operations. * [asyncfile](asyncfile.html) - This module implements asynchronous file reading and writing using - `asyncdispatch`. + An asynchronous file reading and writing using `asyncdispatch`. * [asyncftpclient](asyncftpclient.html) - This module implements an asynchronous FTP client using the `asyncnet` - module. + An asynchronous FTP client using the `asyncnet` module. * [asynchttpserver](asynchttpserver.html) - This module implements an asynchronous HTTP server using the `asyncnet` - module. + An asynchronous HTTP server using the `asyncnet` module. * [asyncmacro](asyncmacro.html) - Implements the `async` and `multisync` macros for `asyncdispatch`. + `async` and `multisync` macros for `asyncdispatch`. * [asyncnet](asyncnet.html) - This module implements asynchronous sockets based on the `asyncdispatch` - module. + Asynchronous sockets based on the `asyncdispatch` module. * [asyncstreams](asyncstreams.html) - This module provides `FutureStream` - a future that acts as a queue. + `FutureStream` - a future that acts as a queue. * [cgi](cgi.html) - This module implements helpers for CGI applications. + Helpers for CGI applications. * [cookies](cookies.html) - This module contains helper procs for parsing and generating cookies. + Helper procs for parsing and generating cookies. * [httpclient](httpclient.html) - This module implements a simple HTTP client which supports both synchronous + A simple HTTP client with support for both synchronous and asynchronous retrieval of web pages. * [mimetypes](mimetypes.html) - This module implements a mimetypes database. + A mimetypes database. * [nativesockets](nativesockets.html) - This module implements a low-level sockets API. + A low-level sockets API. * [net](net.html) - This module implements a high-level sockets API. It replaces the - `sockets` module. + A high-level sockets API. * [selectors](selectors.html) - This module implements a selector API with backends specific to each OS. - Currently, epoll on Linux and select on other operating systems. + A selector API with backends specific to each OS. + Supported OS primitives: `epoll`, `kqueue`, `poll`, and `select` on Windows. * [smtp](smtp.html) - This module implements a simple SMTP client. + A simple SMTP client with support for both synchronous and asynchronous operation. * [socketstreams](socketstreams.html) - This module provides an implementation of the streams interface for sockets. - + An implementation of the streams interface for sockets. * [uri](uri.html) - This module provides functions for working with URIs. + Functions for working with URIs and URLs. Threading --------- * [isolation](isolation.html) - This module implements the `Isolated[T]` type for + The `Isolated[T]` type for safe construction of isolated subgraphs that can be passed efficiently to different channels and threads. * [tasks](tasks.html) - This module provides basic primitives for creating parallel programs. + Basic primitives for creating parallel programs. * [threadpool](threadpool.html) Implements Nim's [spawn](manual_experimental.html#parallel-amp-spawn). @@ -405,13 +396,13 @@ Parsers ------- * [htmlparser](htmlparser.html) - This module parses an HTML document and creates its XML tree representation. + HTML document parser that creates a XML tree representation. * [json](json.html) High-performance JSON parser. * [lexbase](lexbase.html) - This is a low-level module that implements an extremely efficient buffering + A low-level module that implements an extremely efficient buffering scheme for lexers and parsers. This is used by the diverse parsing modules. * [parsecfg](parsecfg.html) @@ -425,7 +416,7 @@ Parsers The `parsecsv` module implements a simple high-performance CSV parser. * [parsejson](parsejson.html) - This module implements a JSON parser. It is used and exported by the [json](json.html) module, but can also be used in its own right. + A JSON parser. It is used and exported by the [json](json.html) module, but can also be used in its own right. * [parseopt](parseopt.html) The `parseopt` module implements a command line option parser. @@ -434,7 +425,7 @@ Parsers The `parsesql` module implements a simple high-performance SQL parser. * [parseutils](parseutils.html) - This module contains helpers for parsing tokens, numbers, identifiers, etc. + Helpers for parsing tokens, numbers, identifiers, etc. * [parsexml](parsexml.html) The `parsexml` module implements a simple high performance XML/HTML parser. @@ -443,7 +434,7 @@ Parsers web can be parsed with it. * [pegs](pegs.html) - This module contains procedures and operators for handling PEGs. + Procedures and operators for handling PEGs. Docutils @@ -455,14 +446,14 @@ Docutils The interface supports one language nested in another. * [packages/docutils/rst](rst.html) - This module implements a reStructuredText parser. A large subset + A reStructuredText parser. A large subset is implemented. Some features of the markdown wiki syntax are also supported. * [packages/docutils/rstast](rstast.html) - This module implements an AST for the reStructuredText parser. + An AST for the reStructuredText parser. * [packages/docutils/rstgen](rstgen.html) - This module implements a generator of HTML/Latex from reStructuredText. + A generator of HTML/Latex from reStructuredText. XML Processing @@ -473,17 +464,17 @@ XML Processing contains a macro for XML/HTML code generation. * [xmlparser](xmlparser.html) - This module parses an XML document and creates its XML tree representation. + XML document parser that creates a XML tree representation. Generators ---------- * [genasts](genasts.html) - This module implements AST generation using captured variables for macros. + AST generation using captured variables for macros. * [htmlgen](htmlgen.html) - This module implements a simple XML and HTML code + A simple XML and HTML code generator. Each commonly used HTML tag has a corresponding macro that generates a string with its HTML representation. @@ -492,14 +483,13 @@ Hashing ------- * [base64](base64.html) - This module implements a Base64 encoder and decoder. + A Base64 encoder and decoder. * [hashes](hashes.html) - This module implements efficient computations of hash values for diverse - Nim types. + Efficient computations of hash values for diverse Nim types. * [md5](md5.html) - This module implements the MD5 checksum algorithm. + The MD5 checksum algorithm. * [oids](oids.html) An OID is a global ID that consists of a timestamp, @@ -507,14 +497,14 @@ Hashing produce a globally distributed unique ID. * [sha1](sha1.html) - This module implements the SHA-1 checksum algorithm. + The SHA-1 checksum algorithm. Serialization ------------- * [jsonutils](jsonutils.html) - This module implements a hookable (de)serialization for arbitrary types + Hookable (de)serialization for arbitrary types using JSON. * [marshal](marshal.html) @@ -526,35 +516,35 @@ Miscellaneous ------------- * [assertions](assertions.html) - This module implements assertion handling. + Assertion handling. * [browsers](browsers.html) - This module implements procs for opening URLs with the user's default + Procs for opening URLs with the user's default browser. * [colors](colors.html) - This module implements color handling for Nim. + Color handling. * [coro](coro.html) - This module implements experimental coroutines in Nim. + Experimental coroutines in Nim. * [decls](decls.html) - This module implements syntax sugar for some declarations. + Syntax sugar for some declarations. * [enumerate](enumerate.html) - This module implements `enumerate` syntactic sugar based on Nim's macro system. + `enumerate` syntactic sugar based on Nim's macro system. * [importutils](importutils.html) Utilities related to import and symbol resolution. * [logging](logging.html) - This module implements a simple logger. + A simple logger. * [segfaults](segfaults.html) Turns access violations or segfaults into a `NilAccessDefect` exception. * [sugar](sugar.html) - This module implements nice syntactic sugar based on Nim's macro system. + Nice syntactic sugar based on Nim's macro system. * [unittest](unittest.html) Implements a Unit testing DSL. @@ -563,10 +553,10 @@ Miscellaneous Decode variable-length integers that are compatible with SQLite. * [with](with.html) - This module implements the `with` macro for easy function chaining. + The `with` macro for easy function chaining. * [wrapnils](wrapnils.html) - This module allows evaluating expressions safely against nil dereferences. + Allows evaluating expressions safely against nil dereferences. Modules for the JavaScript backend @@ -605,12 +595,12 @@ Regular expressions ------------------- * [re](re.html) - This module contains procedures and operators for handling regular + Procedures and operators for handling regular expressions. The current implementation uses PCRE. * [nre](nre.html) - This module contains many help functions for handling regular expressions. + Many help functions for handling regular expressions. The current implementation uses PCRE. Database support @@ -637,7 +627,7 @@ Generic Operating System Services --------------------------------- * [rdstdin](rdstdin.html) - This module contains code for reading from stdin. + Code for reading user input from stdin. Wrappers @@ -651,7 +641,7 @@ Windows-specific ---------------- * [winlean](winlean.html) - Contains a wrapper for a small subset of the Win32 API. + Wrapper for a small subset of the Win32 API. * [registry](registry.html) Windows registry support. @@ -660,7 +650,7 @@ UNIX specific ------------- * [posix](posix.html) - Contains a wrapper for the POSIX standard. + Wrapper for the POSIX standard. * [posix_utils](posix_utils.html) Contains helpers for the POSIX standard or specialized for Linux and BSDs. @@ -676,13 +666,13 @@ Database support ---------------- * [mysql](mysql.html) - Contains a wrapper for the mySQL API. + Wrapper for the mySQL API. * [odbcsql](odbcsql.html) interface to the ODBC driver. * [postgres](postgres.html) - Contains a wrapper for the PostgreSQL API. + Wrapper for the PostgreSQL API. * [sqlite3](sqlite3.html) - Contains a wrapper for the SQLite 3 API. + Wrapper for the SQLite 3 API. Network Programming and Internet Protocols From 2e4ba4ad93c6d9021b6de975cf7ac78e67acba26 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 30 Mar 2023 20:25:14 +0200 Subject: [PATCH 28/55] bump NimVersion to 1.9.3 (#21587) --- lib/system/compilation.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim index c049902774..48521024ef 100644 --- a/lib/system/compilation.nim +++ b/lib/system/compilation.nim @@ -10,7 +10,7 @@ const ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 1 + NimPatch* {.intdefine.}: int = 3 ## is the patch number of Nim's version. ## Odd for devel, even for releases. From d5719c47dce91152173263b08f3b8b12ee04b6ba Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 31 Mar 2023 04:16:09 +0200 Subject: [PATCH 29/55] make --exceptions:quirky work with C++ (#21581) * make --exceptions:quirky work with C++ * make tests green again --- lib/system/excpt.nim | 4 ++-- lib/system/fatal.nim | 6 ++++-- tests/errmsgs/t14444.nim | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 86cfff9cd3..6b40ca3919 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -448,7 +448,7 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = else: pushCurrentException(e) {.emit: "throw `e`;".} - elif defined(nimQuirky) or gotoBasedExceptions: + elif quirkyExceptions or gotoBasedExceptions: pushCurrentException(e) when gotoBasedExceptions: inc nimInErrorMode @@ -560,7 +560,7 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = when defined(cpp) and appType != "lib" and not gotoBasedExceptions and not defined(js) and not defined(nimscript) and hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and - not defined(nimQuirky): + not quirkyExceptions: type StdException {.importcpp: "std::exception", header: "".} = object diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim index 6073ee7795..f1f94d0782 100644 --- a/lib/system/fatal.nim +++ b/lib/system/fatal.nim @@ -9,7 +9,9 @@ {.push profiler: off.} -const gotoBasedExceptions = compileOption("exceptions", "goto") +const + gotoBasedExceptions = compileOption("exceptions", "goto") + quirkyExceptions = compileOption("exceptions", "quirky") when hostOS == "standalone": include "$projectpath/panicoverride" @@ -21,7 +23,7 @@ when hostOS == "standalone": rawoutput(message) panic(arg) -elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript): +elif (quirkyExceptions or defined(nimPanics)) and not defined(nimscript): import ansi_c func name(t: typedesc): string {.magic: "TypeTrait".} diff --git a/tests/errmsgs/t14444.nim b/tests/errmsgs/t14444.nim index 143b4542e8..27365236ed 100644 --- a/tests/errmsgs/t14444.nim +++ b/tests/errmsgs/t14444.nim @@ -3,7 +3,7 @@ discard """ exitcode: "1" output: ''' t14444.nim(13) t14444 -fatal.nim(51) sysFatal +fatal.nim(53) sysFatal Error: unhandled exception: index out of bounds, the container is empty [IndexDefect] ''' """ From 1c7fd717206c79be400f81a05eee771823b880ca Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 31 Mar 2023 15:51:37 +0800 Subject: [PATCH 30/55] fixes changelog (#21590) --- changelog.md | 1 - changelogs/changelog_2_0_0.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 2db577d0bf..100d12fd72 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,6 @@ [//]: # "Additions:" -- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. [//]: # "Deprecations:" diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index a82ce149bf..977dc65233 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -247,6 +247,7 @@ - Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse. - Added `openArray[char]` overloads for `std/unicode` allowing more code reuse. - Added `safe` parameter to `base64.encodeMime`. +- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. [//]: # "Deprecations:" - Deprecated `selfExe` for Nimscript. From a80f1a324fff0b2af47c0766750b3188bcab8041 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 1 Apr 2023 23:08:45 +0800 Subject: [PATCH 31/55] fixes #21592; create type bound operations for calls in the method dispatcher for ORC (#21594) * fixes #21592; create type operations for the method dispatcher * add a test case --- compiler/cgen.nim | 2 +- compiler/cgmeth.nim | 12 ++++++++---- compiler/jsgen.nim | 2 +- tests/arc/tarcmisc.nim | 11 +++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index cc673d0824..61bdab8588 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -2153,7 +2153,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = if m.g.forwardedProcs.len == 0: incl m.flags, objHasKidsValid - let disp = generateMethodDispatchers(graph) + let disp = generateMethodDispatchers(graph, m.idgen) for x in disp: genProcAux(m, x.sym) let mm = m diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 4940a9093a..1ea34f17be 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -11,7 +11,7 @@ import intsets, options, ast, msgs, idents, renderer, types, magicsys, - sempass2, modulegraphs, lineinfos + sempass2, modulegraphs, lineinfos, liftdestructors when defined(nimPreviewSlimSystem): import std/assertions @@ -213,7 +213,7 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) = a[j] = v if h == 1: break -proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym = +proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym = var base = methods[0].ast[dispatcherPos].sym result = base var paramLen = base.typ.len @@ -254,6 +254,10 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PS curr.typ[col], false, g.config) var ret: PNode if retTyp != nil: + createTypeBoundOps(g, nil, retTyp, base.info, idgen) + if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions: + base.flags.incl sfInjectDestructors + var a = newNodeI(nkFastAsgn, base.info) a.add newSymNode(base.ast[resultPos].sym) a.add call @@ -272,7 +276,7 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PS nilchecks.flags.incl nfTransf # should not be further transformed result.ast[bodyPos] = nilchecks -proc generateMethodDispatchers*(g: ModuleGraph): PNode = +proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode = result = newNode(nkStmtList) for bucket in 0.. Date: Sat, 1 Apr 2023 20:29:28 +0200 Subject: [PATCH 32/55] macros: Extend treeTraverse intVal range to nnkUInt64Lit (#21597) * Extend intVal range to nnkUInt64Lit Fixes #21593 * Properly cast intVal as unsigned * Add testcase for #21593 --- lib/core/macros.nim | 2 ++ tests/macros/t21593.nim | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/macros/t21593.nim diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 9cb694bd85..28f52f0a93 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -947,6 +947,8 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent discard # same as nil node in this representation of nnkCharLit .. nnkInt64Lit: res.add(" " & $n.intVal) + of nnkUIntLit .. nnkUInt64Lit: + res.add(" " & $cast[uint64](n.intVal)) of nnkFloatLit .. nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkStrLit .. nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym: diff --git a/tests/macros/t21593.nim b/tests/macros/t21593.nim new file mode 100644 index 0000000000..b0b7ebe757 --- /dev/null +++ b/tests/macros/t21593.nim @@ -0,0 +1,13 @@ +discard """ +nimout: ''' +StmtList + UIntLit 18446744073709551615 + IntLit -1''' +""" + +import macros + +dumpTree: + 0xFFFFFFFF_FFFFFFFF'u + 0xFFFFFFFF_FFFFFFFF + From 63b4b3c5b8c930ffc271c5e4e1a446e8616b2571 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 2 Apr 2023 11:32:36 +0300 Subject: [PATCH 33/55] Fix nim doc crash with group referencing & include (#21600) This fixes a regression introduced in #20990 . When a group referencing is used and one of the overloaded symbols is in `include`d file, then `nim doc` crashes. The fix is in distinguishing (the index of) module and file where the symbol is defined, and using only module as the key in hash table for group referencing. --- compiler/docgen.nim | 17 +++++++++++------ lib/packages/docutils/rst.nim | 14 +++++++++----- .../expected/subdir/subdir_b/utils.html | 13 +++++++++++-- .../expected/subdir/subdir_b/utils.idx | 3 ++- nimdoc/testproject/expected/theindex.html | 2 ++ .../subdir/subdir_b/utils_helpers.nim | 5 +++++ 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 8105cc8072..4a8e380540 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -112,13 +112,16 @@ type proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst) proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str) -proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex = +proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex = let invalid = rstast.FileIndex(-1) - result = d.nimToRstFid.getOrDefault(info.fileIndex, default = invalid) + result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid) if result == invalid: - let fname = toFullPath(d.conf, info) + let fname = toFullPath(d.conf, fileIndex) result = addFilename(d.sharedState, fname) - d.nimToRstFid[info.fileIndex] = result + d.nimToRstFid[fileIndex] = result + +proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex = + addRstFileIndex(d, info.fileIndex) proc cmpDecimalsIgnoreCase(a, b: string): int = ## For sorting with correct handling of cases like 'uint8' and 'uint16'. @@ -1060,7 +1063,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx fileIndex: addRstFileIndex(d, nameNode.info)) addAnchorNim(d.sharedState, external = false, refn = symbolOrId, tooltip = detailedName, langSym = rstLangSymbol, - priority = symbolPriority(k), info = lineinfo) + priority = symbolPriority(k), info = lineinfo, + module = addRstFileIndex(d, FileIndex d.module.position)) let renderFlags = if nonExports: {renderNoBody, renderNoComments, renderDocComments, renderSyms, @@ -1451,7 +1455,8 @@ proc finishGenerateDoc*(d: var PDoc) = isGroup: true), priority = symbolPriority(k), # select index `0` just to have any meaningful warning: - info = overloadChoices[0].info) + info = overloadChoices[0].info, + module = addRstFileIndex(d, FileIndex d.module.position)) if optGenIndexOnly in d.conf.globalOptions: return diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index b3b1fb9c08..6d05a24544 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -350,7 +350,7 @@ type footnoteAnchor = "footnote anchor", headlineAnchor = "implicitly-generated headline anchor" AnchorSubst = object - info: TLineInfo # where the anchor was defined + info: TLineInfo # the file where the anchor was defined priority: int case kind: range[arInternalRst .. arNim] of arInternalRst: @@ -360,6 +360,7 @@ type anchorTypeExt: RstAnchorKind refnameExt: string of arNim: + module: FileIndex # anchor's module (generally not the same as file) tooltip: string # displayed tooltip for Nim-generated anchors langSym: LangSymbol refname: string # A reference name that will be inserted directly @@ -520,6 +521,9 @@ proc getFilename(filenames: RstFileTable, fid: FileIndex): string = proc getFilename(s: PRstSharedState, subst: AnchorSubst): string = getFilename(s.filenames, subst.info.fileIndex) +proc getModule(s: PRstSharedState, subst: AnchorSubst): string = + result = getFilename(s.filenames, subst.module) + proc currFilename(s: PRstSharedState): string = getFilename(s.filenames, s.currFileIdx) @@ -830,7 +834,7 @@ proc addAnchorExtRst(s: var PRstSharedState, key: string, refn: string, proc addAnchorNim*(s: var PRstSharedState, external: bool, refn: string, tooltip: string, langSym: LangSymbol, priority: int, - info: TLineInfo) = + info: TLineInfo, module: FileIndex) = ## Adds an anchor `refn`, which follows ## the rule `arNim` (i.e. a symbol in ``*.nim`` file) s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add( @@ -859,7 +863,7 @@ proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, for subst in substitutions: if subst.kind == arNim: if match(subst.langSym, langSym): - let key: GroupKey = (subst.langSym.symKind, getFilename(s, subst)) + let key: GroupKey = (subst.langSym.symKind, getModule(s, subst)) found.mgetOrPut(key, newSeq[AnchorSubst]()).add subst for key, sList in found: if sList.len == 1: @@ -880,7 +884,7 @@ proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, break doAssert(foundGroup, "docgen has not generated the group for $1 (file $2)" % [ - langSym.name, getFilename(s, sList[0]) ]) + langSym.name, getModule(s, sList[0]) ]) proc findMainAnchorRst(s: PRstSharedState, linkText: string, info: TLineInfo): seq[AnchorSubst] = @@ -3552,7 +3556,7 @@ proc loadIdxFile(s: var PRstSharedState, origFilename: string) = langSym = langSymbolGroup(kind=entry.linkTitle, name=entry.keyword) addAnchorNim(s, external = true, refn = refn, tooltip = entry.linkDesc, langSym = langSym, priority = -4, # lowest - info=info) + info = info, module = info.fileIndex) doAssert s.idxImports[origFilename].title != "" proc preparePass2*(s: var PRstSharedState, mainNode: PRstNode, importdoc = true) = diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index ee2618ab3c..cfdac53107 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -106,6 +106,7 @@
  • fn2()
  • fn2(x: int)
  • fn2(x: int; y: float)
  • +
  • fn2(x: int; y: float; z: float)
    • fn3 @@ -215,7 +216,7 @@
      1. Other case value
      2. Second case.
      -

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

      +

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

      Ref generics like this: binarySearch or binarySearch(openArray[T], K, proc (T, K)) or fN11 or fn11

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

      Pandoc Markdown

      Now repeat all the auto links of above in Pandoc Markdown Syntax.

      -

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

      +

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

      Ref generics like this: binarySearch or binarySearch(openArray[T], K, proc (T, K)) or + +

      +
      proc fn2(x: int; y: float; z: float) {....raises: [], tags: [], forbids: [].}
      +
      + + +
      diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index 81c2103e4b..81b27bcb9f 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -1,5 +1,6 @@ nimTitle utils subdir/subdir_b/utils.html module subdir/subdir_b/utils 0 nim funWithGenerics subdir/subdir_b/utils.html#funWithGenerics,T,U proc funWithGenerics[T, U: SomeFloat](a: T; b: U) 1 +nim fn2 subdir/subdir_b/utils.html#fn2,int,float,float proc fn2(x: int; y: float; z: float) 5 nim enumValueA subdir/subdir_b/utils.html#enumValueA SomeType.enumValueA 45 nim enumValueB subdir/subdir_b/utils.html#enumValueB SomeType.enumValueB 45 nim enumValueC subdir/subdir_b/utils.html#enumValueC SomeType.enumValueC 45 @@ -34,7 +35,7 @@ nim fn subdir/subdir_b/utils.html#fn proc fn[T; U, V: SomeFloat]() 160 nim `'big` subdir/subdir_b/utils.html#'big,string proc `'big`(a: string): SomeType 164 nimgrp $ subdir/subdir_b/utils.html#$-procs-all proc 148 nimgrp fn11 subdir/subdir_b/utils.html#fN11-procs-all proc 85 -nimgrp fn2 subdir/subdir_b/utils.html#fn2-procs-all proc 57 +nimgrp fn2 subdir/subdir_b/utils.html#fn2-procs-all proc 5 nimgrp f subdir/subdir_b/utils.html#f-procs-all proc 130 heading This is now a header subdir/subdir_b/utils.html#this-is-now-a-header This is now a header 0 heading Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next header 0 diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index f9488eb2dd..fd4666bf9a 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -175,6 +175,8 @@ data-doc-search-tag="utils: proc fn2(x: int)" href="subdir/subdir_b/utils.html#fn2%2Cint">utils: proc fn2(x: int)
    • utils: proc fn2(x: int; y: float)
    • +
    • utils: proc fn2(x: int; y: float; z: float)
    fn3: