From eb2f0353bcf46cd250c6bdd7b754904feef3df8a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:30:23 +0800 Subject: [PATCH 001/347] adds missing staticlib hints (#22140) --- compiler/commands.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index f881a4f576c5..cb9a12cbb308 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -240,7 +240,7 @@ proc processCompile(conf: ConfigRef; filename: string) = const errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" - errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" + errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found" errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found" template warningOptionNoop(switch: string) = From ac7b8b678c9e8eff70bb8147d411664470a882fd Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 21 Jun 2023 22:30:55 +0800 Subject: [PATCH 002/347] fixes #21231; template with module as parameter elides usage/checking of module name specifier (#22109) * fixes #21231; template with module as parameter elides usage/checking of module name specifier * add a test case --- compiler/lookups.nim | 3 +++ tests/template/t21231.nim | 10 ++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/template/t21231.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index dce841e2f222..17eedca924b7 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -623,6 +623,9 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = errorUndeclaredIdentifierHint(c, n[1], ident) elif n[1].kind == nkSym: result = n[1].sym + if result.owner != nil and result.owner != m and checkUndeclared in flags: + # dotExpr in templates can end up here + result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1])) elif checkUndeclared in flags and n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}: localError(c.config, n[1].info, "identifier expected, but got: " & diff --git a/tests/template/t21231.nim b/tests/template/t21231.nim new file mode 100644 index 000000000000..51e96cdd651e --- /dev/null +++ b/tests/template/t21231.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "undeclared identifier: 'x'" +""" + +var x: int +# bug #21231 +template f(y: untyped) = echo y.x +for i in 1 .. 10: + x = i + f(system) From d137a3b52af1a83cd9617e47b63d7b3d6c4d935b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 22 Jun 2023 21:58:10 +0800 Subject: [PATCH 003/347] fixes :idx: index in in modules (2.0 regression) and in markdown files (persistent issues since 0.20.2) (#22141) fixes :idx: index --- lib/packages/docutils/rstgen.nim | 12 +++++++++--- lib/packages/docutils/rstidx.nim | 9 ++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 57bc00fcbd3c..ec9926863cde 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -591,10 +591,16 @@ proc readIndexDir*(dir: string): if path.endsWith(IndexExt): var (fileEntries, title) = parseIdxFile(path) # Depending on type add this to the list of symbols or table of APIs. - if title.kind == ieNimTitle: + if title.kind in {ieNimTitle, ieIdxRole}: for i in 0 ..< fileEntries.len: - if fileEntries[i].kind != ieNim: - continue + if title.kind == ieIdxRole: + # Don't add to symbols TOC entries (they start with a whitespace). + let toc = fileEntries[i].linkTitle + if toc.len > 0 and toc[0] == ' ': + continue + else: + if fileEntries[i].kind != ieNim: + continue # Ok, non TOC entry, add it. setLen(result.symbols, L + 1) result.symbols[L] = fileEntries[i] diff --git a/lib/packages/docutils/rstidx.nim b/lib/packages/docutils/rstidx.nim index c109636d785d..c48f44300cfe 100644 --- a/lib/packages/docutils/rstidx.nim +++ b/lib/packages/docutils/rstidx.nim @@ -109,16 +109,19 @@ proc parseIdxFile*(path: string): result.fileEntries[f].kind = parseIndexEntryKind(cols[0]) result.fileEntries[f].keyword = cols[1] result.fileEntries[f].link = cols[2] - if result.title.keyword.len == 0: + if result.fileEntries[f].kind == ieIdxRole: result.fileEntries[f].module = base else: - result.fileEntries[f].module = result.title.keyword + if result.title.keyword.len == 0: + result.fileEntries[f].module = base + else: + result.fileEntries[f].module = result.title.keyword result.fileEntries[f].linkTitle = cols[3].unquoteIndexColumn result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn result.fileEntries[f].line = parseInt(cols[5]) - if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}: + if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle, ieIdxRole}: result.title = result.fileEntries[f] inc f From 88114948c41f38d7366dc8d80abc09f00c2492fa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 23 Jun 2023 04:17:23 +0800 Subject: [PATCH 004/347] fixes #21110; duplicate proc definitions for inline iters (#21136) fixes #21110; duplicate proc definitions for iters --- compiler/transf.nim | 8 ++++++++ tests/iter/titer.nim | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/compiler/transf.nim b/compiler/transf.nim index 24363cad0be7..0a28fd1a05bd 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -324,6 +324,14 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode = if a.kind == nkSym: n[1] = transformSymAux(c, a) return n + of nkProcDef: # todo optimize nosideeffects? + result = newTransNode(n) + let x = freshVar(c, n[namePos].sym) + idNodeTablePut(c.transCon.mapping, n[namePos].sym, x) + result[namePos] = x # we have to copy proc definitions for iters + for i in 1.. Date: Sat, 24 Jun 2023 07:13:15 +0100 Subject: [PATCH 005/347] adds nimbasePattern compiler option (#22144) adds optonal --nimbasepattern --- compiler/cgen.nim | 4 +++- compiler/commands.nim | 2 ++ compiler/options.nim | 1 + tests/options/tnimbasepattern.nim | 26 ++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/options/tnimbasepattern.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e79081dc6d74..0450625fcd3f 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -935,7 +935,9 @@ proc cgsymValue(m: BModule, name: string): Rope = result.addActualSuffixForHCR(m.module, sym) proc generateHeaders(m: BModule) = - m.s[cfsHeaders].add("\L#include \"nimbase.h\"\L") + var nimbase = m.config.nimbasePattern + if nimbase == "": nimbase = "nimbase.h" + m.s[cfsHeaders].addf("\L#include \"$1\"\L", [nimbase]) for it in m.headerFiles: if it[0] == '#': diff --git a/compiler/commands.nim b/compiler/commands.nim index cb9a12cbb308..333a0f0d031f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -829,6 +829,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "header": if conf != nil: conf.headerFile = arg incl(conf.globalOptions, optGenIndex) + of "nimbasepattern": + if conf != nil: conf.nimbasePattern = arg of "index": case arg.normalize of "", "on": conf.globalOptions.incl {optGenIndex} diff --git a/compiler/options.nim b/compiler/options.nim index 082caedf18b2..d3cf71d4f6ff 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -333,6 +333,7 @@ type cppDefines*: HashSet[string] # (*) headerFile*: string + nimbasePattern*: string # pattern to find nimbase.h features*: set[Feature] legacyFeatures*: set[LegacyFeature] arguments*: string ## the arguments to be passed to the program that diff --git a/tests/options/tnimbasepattern.nim b/tests/options/tnimbasepattern.nim new file mode 100644 index 000000000000..1237af5c59bd --- /dev/null +++ b/tests/options/tnimbasepattern.nim @@ -0,0 +1,26 @@ +discard """ + cmd: "nim cpp --nimbasepattern:test.h --cincludes:./tests/options $file " + output:''' +(a: 1) +''' +""" +const header = """ +#pragma once +#include "nimbase.h" +struct Foo { + int a; +}; +""" + +import os +static: + const dir = "./tests/options/" + createDir(dir) + writeFile(dir / "test.h", header) + +type + Foo {.importc.} = object + a: int32 = 1 + + +echo $Foo() \ No newline at end of file From c6c85f84db3bd7bd2d1c5823020c7df007f1bb69 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Sat, 24 Jun 2023 08:13:43 +0200 Subject: [PATCH 006/347] macOS `ar` doesn't support `@` syntax (#22146) When the linker command line is long, Nim compiler generates a file for passing the linker arguments. On `macOS`, that mechanism fails as the `@` syntax is not supported by `ar`. Use `xargs` instead to pass the linker arguments file. --- compiler/extccomp.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 040fe35e1943..832242456a8a 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -834,7 +834,10 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) = else: writeFile(linkerArgs, args) try: - execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs) + when defined(macosx): + execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs) + else: + execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs) finally: removeFile(linkerArgs) From f718f295df3f6ee5d7fd6fc19e39ac663821b00a Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 25 Jun 2023 01:01:08 +0300 Subject: [PATCH 007/347] fix VM uint conversion size bug, stricter int gen on JS (#22150) * fix VM uint conversion bug, stricter int gen on JS fixes #19929 * fix float -> uint64 conversion too * no need to mask to source type * simpler diff with explanation, add test for described issue --- compiler/jsgen.nim | 22 ++++++++++++++++------ compiler/vm.nim | 10 +++++++--- tests/js/tneginthash.nim | 21 +++++++++++++++++++++ tests/stdlib/thashes.nim | 3 +++ tests/vm/ttouintconv.nim | 7 +++++++ 5 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 tests/js/tneginthash.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ecfc221086e2..1fbf6c74c70e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2513,10 +2513,12 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = elif src.kind == tyUInt64: r.res = "BigInt.asIntN(64, $1)" % [r.res] elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - if fromInt or fromUint: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] elif src.kind in {tyFloat..tyFloat64}: - r.res = "BigInt(Math.trunc($1))" % [r.res] + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] elif src.kind == tyInt64: r.res = "BigInt.asUintN(64, $1)" % [r.res] elif toUint or dest.kind in tyFloat..tyFloat64: @@ -2755,10 +2757,12 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = elif src.kind == tyUInt64: r.res = "BigInt.asIntN(64, $1)" % [r.res] elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - if fromInt or fromUint: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] elif src.kind in {tyFloat..tyFloat64}: - r.res = "BigInt(Math.trunc($1))" % [r.res] + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] elif src.kind == tyInt64: r.res = "BigInt.asUintN(64, $1)" % [r.res] elif dest.kind in tyFloat..tyFloat64: @@ -2790,11 +2794,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if optJsBigInt64 in p.config.globalOptions: r.res.add('n') of tyInt64: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal if optJsBigInt64 in p.config.globalOptions: r.res.add('n') + if wrap: r.res.add ')' else: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal + if wrap: r.res.add ')' r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): diff --git a/compiler/vm.nim b/compiler/vm.nim index 8b5faabfe617..7376ff165ee8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -443,12 +443,16 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): of tyFloat..tyFloat64: dest.intVal = int(src.floatVal) else: - let srcSize = getSize(c.config, styp) let destSize = getSize(c.config, desttyp) - let srcDist = (sizeof(src.intVal) - srcSize) * 8 let destDist = (sizeof(dest.intVal) - destSize) * 8 var value = cast[BiggestUInt](src.intVal) - value = (value shl srcDist) shr srcDist + when false: + # this would make uint64(-5'i8) evaluate to 251 + # but at runtime, uint64(-5'i8) is 18446744073709551611 + # so don't do it + let srcSize = getSize(c.config, styp) + let srcDist = (sizeof(src.intVal) - srcSize) * 8 + value = (value shl srcDist) shr srcDist value = (value shl destDist) shr destDist dest.intVal = cast[BiggestInt](value) of tyBool: diff --git a/tests/js/tneginthash.nim b/tests/js/tneginthash.nim new file mode 100644 index 000000000000..c082405c9f2c --- /dev/null +++ b/tests/js/tneginthash.nim @@ -0,0 +1,21 @@ +# issue #19929 + +import std/[tables, hashes] + +type Foo = object + a: int + +proc hash(f: Foo): Hash = + var h: Hash = 0 + h = h !& hash(f.a) + result = !$h + +proc transpose[T, S](data: array[T, S]): Table[S, T] = + for i, x in data: + result[x] = i + +const xs = [Foo(a: 5), Foo(a: -5)] +const x = transpose(xs) + +doAssert x[Foo(a: -5)] == 1 +doAssert x[Foo(a: 5)] == 0 diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 6b5e055b4b06..b6fbbbdb7c83 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -29,6 +29,9 @@ block hashes: const wy123 = hashWangYi1(123) doAssert wy123 != 0 doAssert hashWangYi1(123) == wy123 + const wyNeg123 = hashWangYi1(-123) + doAssert wyNeg123 != 0 + doAssert hashWangYi1(-123) == wyNeg123 # "hashIdentity value incorrect at 456" diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim index ff2187a36452..8c43a3adb1a0 100644 --- a/tests/vm/ttouintconv.nim +++ b/tests/vm/ttouintconv.nim @@ -75,3 +75,10 @@ macro foo2() = foo() foo2() + +block: + const neg5VM = block: + let x = -5'i8 + uint64(x) + let y = -5'i8 + doAssert uint64(y) == neg5VM From 20037a47499ea183afb0e8d2a3f68c2b2952aa5d Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 25 Jun 2023 17:52:16 +0300 Subject: [PATCH 008/347] make `var object` match better than `object` (#22152) * fix `var object` not matching better than `object` fixes #13302 * remove comment for brevity * try note * try minimize breaks --- compiler/sigmatch.nim | 4 +++- tests/overload/tvartypeclass.nim | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/overload/tvartypeclass.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5786a320c6d9..f94b4ee60e2b 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -215,7 +215,7 @@ proc sumGeneric(t: PType): int = # and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more # specific than Foo[T]. var t = t - var isvar = 1 + var isvar = 0 while true: case t.kind of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray, @@ -251,6 +251,8 @@ proc sumGeneric(t: PType): int = of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64, tyCompositeTypeClass: + return isvar + 1 + of tyBuiltInTypeClass: return isvar else: return 0 diff --git a/tests/overload/tvartypeclass.nim b/tests/overload/tvartypeclass.nim new file mode 100644 index 000000000000..04f3f5a910c7 --- /dev/null +++ b/tests/overload/tvartypeclass.nim @@ -0,0 +1,11 @@ +# issue #13302 + +proc foo(x: object): int = x.i*2 +proc foo(x: var object) = x.i*=2 +type Foo = object + i: int +let x = Foo(i: 3) +var y = Foo(i: 4) +doAssert foo(x) == 6 +foo(y) +doAssert y.i == 8 From 942c37865976e1535855e9206860d7db1c18b2a0 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:15:47 +0800 Subject: [PATCH 009/347] =?UTF-8?q?fixes=20#22148;=20std/memfiles.memSlice?= =?UTF-8?q?s=20nesting=20now=20fails=20with=20memory=20sa=E2=80=A6=20(#221?= =?UTF-8?q?54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixes #22148; std/memfiles.memSlices nesting now fails with memory safety capture violation * adds a test case --- compiler/transf.nim | 2 +- tests/iter/t22148.nim | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/iter/t22148.nim diff --git a/compiler/transf.nim b/compiler/transf.nim index 0a28fd1a05bd..6ff1da899ae7 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -326,7 +326,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode = return n of nkProcDef: # todo optimize nosideeffects? result = newTransNode(n) - let x = freshVar(c, n[namePos].sym) + let x = newSymNode(copySym(n[namePos].sym, c.idgen)) idNodeTablePut(c.transCon.mapping, n[namePos].sym, x) result[namePos] = x # we have to copy proc definitions for iters for i in 1.. Date: Sun, 25 Jun 2023 20:37:21 +0200 Subject: [PATCH 010/347] rm zero-extension and uint conversions deprecated since 0.19.9 (#22151) --- lib/system/arithmetics.nim | 56 -------------------------------------- 1 file changed, 56 deletions(-) diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index 5c21e56ef690..a1cb935ce0e1 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -403,59 +403,3 @@ proc `%%`*(x, y: int8): int8 {.inline.} = cast[int8](cast[uint8](x) mod cast[u proc `%%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) mod cast[uint16](y)) proc `%%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) mod cast[uint32](y)) proc `%%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) mod cast[uint64](y)) - -when not defined(nimPreviewSlimSystem): - proc ze*(x: int8): int {.deprecated.} = - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int](uint(cast[uint8](x))) - - proc ze*(x: int16): int {.deprecated.} = - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int](uint(cast[uint16](x))) - - proc ze64*(x: int8): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint8](x))) - - proc ze64*(x: int16): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint16](x))) - - proc ze64*(x: int32): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint32](x))) - - proc ze64*(x: int): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. Does nothing if the size of an `int` is the same as `int64`. - ## (This is the case on 64 bit processors.) - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint](x))) - - proc toU8*(x: int): int8 {.deprecated.} = - ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits - ## from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int8](x) - - proc toU16*(x: int): int16 {.deprecated.} = - ## treats `x` as unsigned and converts it to an `int16` by taking the last - ## 16 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int16](x) - - proc toU32*(x: int64): int32 {.deprecated.} = - ## treats `x` as unsigned and converts it to an `int32` by taking the - ## last 32 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int32](x) From 43a3de9077955031e55691ebb84ee958f6aa260e Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Mon, 26 Jun 2023 23:07:42 +1000 Subject: [PATCH 011/347] Fix regression in `std/times` (#22155) * Add simple test case Just so the regression doesn't happen again * Specify initDateTime is gcsafe in the forward declarations --- lib/pure/times.nim | 4 ++-- tests/stdlib/ttimes.nim | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index bea1ac760bf2..4f7af657cf79 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1529,11 +1529,11 @@ proc getClockStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect]. proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear, hour: HourRange, minute: MinuteRange, second: SecondRange, nanosecond: NanosecondRange, - zone: Timezone = local()): DateTime {.raises: [], tags: [], since: (1, 5).} + zone: Timezone = local()): DateTime {.gcsafe, raises: [], tags: [], since: (1, 5).} proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear, hour: HourRange, minute: MinuteRange, second: SecondRange, - zone: Timezone = local()): DateTime {.raises: [], tags: [], since: (1, 5).} + zone: Timezone = local()): DateTime {.gcsafe, raises: [], tags: [], since: (1, 5).} # # TimeFormat diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index 5794fa739ed5..e01ab3a4ff59 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -766,3 +766,7 @@ block: # ttimes check dt.format("GGGG") == "2023" check dt.format("dddd 'KW'V GGGG") == "Thursday KW1 2023" + block: # Can be used inside gcsafe proc + proc test(): DateTime {.gcsafe.} = + result = "1970".parse("yyyy") + doAssert test().year == 1970 From 4546f5dfe539ff4f37a5100bdfedaf4867293f3a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:10:06 +0800 Subject: [PATCH 012/347] adds T destructor for refs (#22147) * adds T destructor for refs * add `newRefdestructor` * adds ref overload for destructors * fixes config --- compiler/liftdestructors.nim | 7 ++++++- lib/system.nim | 4 ++++ tests/config.nims | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index e401934815f5..c25f089fde0a 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -1068,7 +1068,12 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info) let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), idgen, result, info) - dest.typ = makeVarType(typ.owner, typ, idgen) + + if kind == attachedDestructor and typ.kind == tyRef: + dest.typ = typ + else: + dest.typ = makeVarType(typ.owner, typ, idgen) + if kind == attachedTrace: src.typ = getSysType(g, info, tyPointer) else: diff --git a/lib/system.nim b/lib/system.nim index 2290ff6f6a54..d4835b20b967 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -365,6 +365,10 @@ proc arrGet[I: Ordinal;T](a: T; i: I): T {. proc arrPut[I: Ordinal;T,S](a: T; i: I; x: S) {.noSideEffect, magic: "ArrPut".} +when defined(nimAllowNonVarDestructor): + proc `=destroy`*[T](x: ref T) {.inline, magic: "Destroy".} = + discard + proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} = ## Generic `destructor`:idx: implementation that can be overridden. discard diff --git a/tests/config.nims b/tests/config.nims index 842459c3a809..b288bec2ae11 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -43,3 +43,4 @@ switch("define", "nimPreviewRangeDefault") switch("warningAserror", "UnnamedBreak") switch("legacy", "verboseTypeMismatch") + From 4ce3a68e794fe3d448928ccbbc34191ce24c4fbd Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 27 Jun 2023 08:03:52 +0800 Subject: [PATCH 013/347] fixes #22163; use `{.push warning[BareExcept]:off.}` to override settings temporarily (#21390) * use `{.push warning[BareExcept]:off.}` to override settings temporarily * likewise, suppress expect --- lib/pure/unittest.nim | 12 ++++++++---- lib/std/assertions.nim | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 964fba0e4be7..afe98ca4e2ad 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -550,14 +550,14 @@ template test*(name, body) {.dirty.} = for formatter in formatters: formatter.testStarted(name) - {.warning[BareExcept]:off.} + {.push warning[BareExcept]:off.} try: when declared(testSetupIMPLFlag): testSetupIMPL() when declared(testTeardownIMPLFlag): defer: testTeardownIMPL() - {.warning[BareExcept]:on.} + {.push warning[BareExcept]:on.} body - {.warning[BareExcept]:off.} + {.pop.} except: let e = getCurrentException() @@ -579,7 +579,7 @@ template test*(name, body) {.dirty.} = ) testEnded(testResult) checkpoints = @[] - {.warning[BareExcept]:on.} + {.pop.} proc checkpoint*(msg: string) = ## Set a checkpoint identified by `msg`. Upon test failure all @@ -767,8 +767,11 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = defectiveRobot() template expectBody(errorTypes, lineInfoLit, body): NimNode {.dirty.} = + {.push warning[BareExcept]:off.} try: + {.push warning[BareExcept]:on.} body + {.pop.} checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.") fail() except errorTypes: @@ -776,6 +779,7 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = except: checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.") fail() + {.pop.} var errorTypes = newNimNode(nnkBracket) for exp in exceptions: diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 3dca644ad6c1..56c37d205725 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -98,7 +98,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) = const begin = "expected raising '" & astToStr(exception) & "', instead" const msgEnd = " by: " & astToStr(code) template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd) - {.warning[BareExcept]:off.} + {.push warning[BareExcept]:off.} when Exception is exception: try: if true: @@ -117,6 +117,6 @@ template doAssertRaises*(exception: typedesc, code: untyped) = mixin `$` # alternatively, we could define $cstring in this module raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) except: raisedForeign() - {.warning[BareExcept]:on.} + {.pop.} if wrong: raiseAssert(begin & " nothing was raised" & msgEnd) From 4b761295e7ab31412903c02b8fb98f580bf005ca Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 27 Jun 2023 08:19:17 +0200 Subject: [PATCH 014/347] fix struct stat (#22161) Undo damage of https://github.com/nim-lang/Nim/pull/14170 --- lib/posix/posix_linux_amd64.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 4eb357d62030..91c290a44251 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -241,6 +241,7 @@ type st_atim*: Timespec ## Time of last access. st_mtim*: Timespec ## Time of last data modification. st_ctim*: Timespec ## Time of last status change. + reserved: array[3, clong] Statvfs* {.importc: "struct statvfs", header: "", final, pure.} = object ## struct statvfs From cb40f11e6c0a8f8df8b429f239483c408c7cc668 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 27 Jun 2023 08:20:02 +0200 Subject: [PATCH 015/347] uint arithmetic for pointers (#22159) pointers are not signed and arithmetic may correctly cross int.max threshold this PR only fixes 2 occurances - there are plenty however in the std lib --- lib/system/alloc.nim | 2 +- lib/system/mm/malloc.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index ac1741ceb5d8..edb094f3349a 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -1139,7 +1139,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer = result = realloc(allocator, p, newSize) if newSize > oldSize: - zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize) + zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize) when false: proc countFreeMem(): int = diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index d41dce705fab..b24b6f1e0c3c 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -22,7 +22,7 @@ proc reallocImpl(p: pointer, newSize: Natural): pointer = proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer = result = realloc(p, newSize.csize_t) if newSize > oldSize: - zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize) + zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize) proc deallocImpl(p: pointer) = c_free(p) From 47635d30315595fc2dbe2162b691aff0f8e0f459 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 27 Jun 2023 10:09:04 +0200 Subject: [PATCH 016/347] fix Sigaction struct definition (#22160) SigInfo is still wrong (most of its fields are in a union) --- lib/posix/posix_linux_amd64.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 91c290a44251..a039d3808a8a 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -310,7 +310,7 @@ type sa_mask*: Sigset ## Set of signals to be blocked during execution of ## the signal handling function. sa_flags*: cint ## Special flags. - sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.} + sa_restorer: proc() {.noconv.} ## not intended for application use. Stack* {.importc: "stack_t", header: "", final, pure.} = object ## stack_t @@ -326,9 +326,9 @@ type SigInfo* {.importc: "siginfo_t", header: "", final, pure.} = object ## siginfo_t si_signo*: cint ## Signal number. - si_code*: cint ## Signal code. si_errno*: cint ## If non-zero, an errno value associated with ## this signal, as defined in . + si_code*: cint ## Signal code. si_pid*: Pid ## Sending process ID. si_uid*: Uid ## Real user ID of sending process. si_addr*: pointer ## Address of faulting instruction. @@ -337,6 +337,12 @@ type si_value*: SigVal ## Signal value. pad {.importc: "_pad".}: array[128 - 56, uint8] +template sa_sigaction*(v: Sigaction): proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.} = + cast[proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}](v.sa_handler) +proc `sa_sigaction=`*(v: var Sigaction, x: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}) = + v.sa_handler = cast[proc (x: cint) {.noconv.}](x) + +type Nl_item* {.importc: "nl_item", header: "".} = cint Nl_catd* {.importc: "nl_catd", header: "".} = pointer From d52b1d848e6e402971e9bd81e58f6d2553854372 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jun 2023 12:55:47 +0200 Subject: [PATCH 017/347] destructors: update, =destroy does not require a 'var T' (#22168) --- doc/destructors.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/destructors.md b/doc/destructors.md index 789cb93d6d51..12def00537f0 100644 --- a/doc/destructors.md +++ b/doc/destructors.md @@ -36,7 +36,7 @@ written as: len, cap: int data: ptr UncheckedArray[T] - proc `=destroy`*[T](x: var myseq[T]) = + proc `=destroy`*[T](x: myseq[T]) = if x.data != nil: for i in 0.. Date: Tue, 27 Jun 2023 19:07:29 +0800 Subject: [PATCH 018/347] adds =destroy T support for strings and seqs (#22167) * adds =destroy T support for strings and seqs * fixes system * fixes tests --- compiler/liftdestructors.nim | 4 ++-- lib/system.nim | 8 +++++++- tests/arc/topt_no_cursor.nim | 2 +- tests/destructor/tv2_cast.nim | 12 ++++++------ 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index c25f089fde0a..eac2323aa933 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -1069,7 +1069,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), idgen, result, info) - if kind == attachedDestructor and typ.kind == tyRef: + if kind == attachedDestructor and typ.kind in {tyRef, tyString, tySequence} and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: dest.typ = typ else: dest.typ = makeVarType(typ.owner, typ, idgen) @@ -1196,7 +1196,7 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id if op != nil: if op.ast.isGenericRoutine: internalError(g.config, info, "resolved destructor is generic") - if op.magic == mDestroy: + if op.magic == mDestroy and t.kind != tyString: internalError(g.config, info, "patching mDestroy with mDestroy?") n[0] = newSymNode(op) for x in n: patchBody(g, c, x, info, idgen) diff --git a/lib/system.nim b/lib/system.nim index d4835b20b967..845ab58e66de 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -365,7 +365,13 @@ proc arrGet[I: Ordinal;T](a: T; i: I): T {. proc arrPut[I: Ordinal;T,S](a: T; i: I; x: S) {.noSideEffect, magic: "ArrPut".} -when defined(nimAllowNonVarDestructor): +when defined(nimAllowNonVarDestructor) and arcLikeMem: + proc `=destroy`*(x: string) {.inline, magic: "Destroy".} = + discard + + proc `=destroy`*[T](x: seq[T]) {.inline, magic: "Destroy".} = + discard + proc `=destroy`*[T](x: ref T) {.inline, magic: "Destroy".} = discard diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 39e5c8a9b887..7cfb0a0d50bd 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -95,7 +95,7 @@ try: finally: `=destroy`(splitted) finally: - `=destroy_1`(lan_ip) + `=destroy`(lan_ip) -- end of expandArc ------------------------ --expandArc: mergeShadowScope diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index 4ff2dc9a07f1..6fcb5994c3a4 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -20,8 +20,8 @@ data = :tmpD_2)) :tmpD `=destroy`(:tmpD_2) -`=destroy_1`(:tmpD_1) -`=destroy_1`(data) +`=destroy`(:tmpD_1) +`=destroy`(data) -- end of expandArc ------------------------ --expandArc: main1 @@ -37,8 +37,8 @@ data = :tmpD_1)) :tmpD `=destroy`(:tmpD_1) -`=destroy_1`(data) -`=destroy_1`(s) +`=destroy`(data) +`=destroy`(s) -- end of expandArc ------------------------ --expandArc: main2 @@ -54,7 +54,7 @@ data = :tmpD_1)) :tmpD `=destroy`(:tmpD_1) -`=destroy_1`(data) +`=destroy`(data) `=destroy`(s) -- end of expandArc ------------------------ --expandArc: main3 @@ -73,7 +73,7 @@ data = :tmpD `=destroy`(:tmpD_2) `=destroy`(:tmpD_1) -`=destroy_1`(data) +`=destroy`(data) -- end of expandArc ------------------------ ''' """ From faa59b1e1f9b04149f77e34bdaba982d2724e7b4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jun 2023 14:12:21 +0200 Subject: [PATCH 019/347] Use pinned Atlas commit (#22169) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 2fde9fbd1392..a10e01dbbe98 100644 --- a/koch.nim +++ b/koch.nim @@ -12,7 +12,7 @@ const # examples of possible values for repos: Head, ea82b54 NimbleStableCommit = "168416290e49023894fc26106799d6f1fc964a2d" # master - AtlasStableCommit = "master" + AtlasStableCommit = "7b780811a168f3f32bff4822369dda46a7f87f9a" ChecksumsStableCommit = "b4c73320253f78e3a265aec6d9e8feb83f97c77b" # examples of possible values for fusion: #head, #ea82b54, 1.2.3 From ce0909f04729a45f8e56e099fae2471e0504e0ff Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jun 2023 14:58:02 +0200 Subject: [PATCH 020/347] fixes #22137 (#22170) --- compiler/seminst.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b4b5ad0a2371..5b0fe6ba25e4 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -352,7 +352,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, openScope(c) let gp = n[genericParamsPos] - internalAssert c.config, gp.kind == nkGenericParams + if gp.kind != nkGenericParams: + # bug #22137 + globalError(c.config, info, "generic instantiation too nested") n[namePos] = newSymNode(result) pushInfoContext(c.config, info, fn.detailedInfo) var entry = TInstantiation.new From 9616762cfe7a6d1607ba7d00e00537e9620aff50 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jun 2023 16:46:41 +0200 Subject: [PATCH 021/347] Revert "fix struct stat" (#22171) Revert "fix struct stat (#22161)" This reverts commit 4b761295e7ab31412903c02b8fb98f580bf005ca. --- lib/posix/posix_linux_amd64.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index a039d3808a8a..8d11c507d70a 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -241,7 +241,6 @@ type st_atim*: Timespec ## Time of last access. st_mtim*: Timespec ## Time of last data modification. st_ctim*: Timespec ## Time of last status change. - reserved: array[3, clong] Statvfs* {.importc: "struct statvfs", header: "", final, pure.} = object ## struct statvfs From 427ad17161a19ecf8d0e2fc932064e18226210fa Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jun 2023 22:42:48 +0200 Subject: [PATCH 022/347] fixes #22001 (#22177) * fixes #22001 * added test case --- compiler/dfa.nim | 6 ++++-- tests/arc/tmove_regression.nim | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/arc/tmove_regression.nim diff --git a/compiler/dfa.nim b/compiler/dfa.nim index d145e31c33af..b21fcf473c07 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -264,7 +264,8 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) = c.blocks[i].raiseFixups.add lab1 else: var trailingFinales: seq[PNode] - if c.inTryStmt > 0: #Ok, we are in a try, lets see which (if any) try's we break out from: + if c.inTryStmt > 0: + # Ok, we are in a try, lets see which (if any) try's we break out from: for b in countdown(c.blocks.high, i): if c.blocks[b].isTryBlock: trailingFinales.add c.blocks[b].finale @@ -385,7 +386,8 @@ proc genCall(c: var Con; n: PNode) = # Pass by 'out' is a 'must def'. Good enough for a move optimizer. genDef(c, n[i]) # every call can potentially raise: - if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]): + if c.inTryStmt > 0 and canRaiseConservative(n[0]): + inc c.interestingInstructions # we generate the instruction sequence: # fork lab1 # goto exceptionHandler (except or finally) diff --git a/tests/arc/tmove_regression.nim b/tests/arc/tmove_regression.nim new file mode 100644 index 000000000000..7d9a867c3916 --- /dev/null +++ b/tests/arc/tmove_regression.nim @@ -0,0 +1,22 @@ +discard """ + output: '''/1/2 +/1 +/ +''' +"""" + +# bug #22001 + +import std / [os, strutils] + +proc finOp2(path, name: string): (string, File) = # Find & open FIRST `name` + var current = path + while true: + if current.isRootDir: break # <- current=="" => current.isRootDir + current = current.parentDir + let dir = current + echo dir.replace('\\', '/') # Commenting out try/except below hides bug + try: result[0] = dir/name; result[1] = open(result[0]); return + except CatchableError: discard + +discard finOp2("/1/2/3", "4") # All same if this->inside a proc From d195877615f3029a4931e844d81b45901a4cf5c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Wed, 28 Jun 2023 16:49:35 +0100 Subject: [PATCH 023/347] docs nimBasePattern (#22179) --- doc/advopt.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/advopt.txt b/doc/advopt.txt index 5465e3a62cf5..8362bbf4cba4 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -175,3 +175,5 @@ Advanced options: --deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc`` --jsbigint64:on|off toggle the use of BigInt for 64-bit integers for the JavaScript backend (default: on) + --nimBasePattern:nimbase.h + allows to specify a custom pattern for `nimbase.h` From b35942ef8386d082b8564b2aa62843a4949cdb60 Mon Sep 17 00:00:00 2001 From: metagn Date: Wed, 28 Jun 2023 23:38:08 +0300 Subject: [PATCH 024/347] fix new type inference for `noreturn` [backport] (#22182) fixes #22180 Backported since apparently the new type inference was backported --- compiler/semstmts.nim | 21 ++++++++++++------- tests/types/ttopdowninference.nim | 35 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 34df83f46037..836be6e4a998 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -187,13 +187,15 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): it[0] = forceBool(c, semExprWithType(c, it[0], expectedType = getSysType(c.graph, n.info, tyBool))) it[1] = semExprBranch(c, it[1], flags, expectedType) typ = commonType(c, typ, it[1]) - expectedType = typ + if not endsInNoReturn(it[1]): + expectedType = typ closeScope(c) elif it.len == 1: hasElse = true it[0] = semExprBranchScope(c, it[0], expectedType) typ = commonType(c, typ, it[0]) - expectedType = typ + if not endsInNoReturn(it[0]): + expectedType = typ else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or (not hasElse and efInTypeof notin flags): @@ -234,7 +236,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) var expectedType = expectedType n[0] = semExprBranchScope(c, n[0], expectedType) typ = commonType(c, typ, n[0].typ) - expectedType = typ + if not endsInNoReturn(n[0]): + expectedType = typ var last = n.len - 1 var catchAllExcepts = 0 @@ -295,7 +298,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) if a.kind != nkFinally: a[^1] = semExprBranchScope(c, a[^1], expectedType) typ = commonType(c, typ, a[^1]) - expectedType = typ + if not endsInNoReturn(a[^1]): + expectedType = typ else: a[^1] = semExprBranchScope(c, a[^1]) dec last @@ -1145,7 +1149,8 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil var last = x.len-1 x[last] = semExprBranchScope(c, x[last], expectedType) typ = commonType(c, typ, x[last]) - expectedType = typ + if not endsInNoReturn(x[last]): + expectedType = typ of nkElifBranch: if hasElse: invalidOrderOfBranches(x) chckCovered = false @@ -1154,13 +1159,15 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool))) x[1] = semExprBranch(c, x[1], expectedType = expectedType) typ = commonType(c, typ, x[1]) - expectedType = typ + if not endsInNoReturn(x[1]): + expectedType = typ closeScope(c) of nkElse: checkSonsLen(x, 1, c.config) x[0] = semExprBranchScope(c, x[0], expectedType) typ = commonType(c, typ, x[0]) - expectedType = typ + if not endsInNoReturn(x[0]): + expectedType = typ if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse: message(c.config, x.info, warnUnreachableElse) hasElse = true diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim index cabc798edfbf..2a26e0e34e09 100644 --- a/tests/types/ttopdowninference.nim +++ b/tests/types/ttopdowninference.nim @@ -290,3 +290,38 @@ block: # bug #21377 {:} doAssert b(0) == {:} + +block: # bug #22180 + type A = object + proc j() = discard + + let x = + if false: + (ref A)(nil) + else: + if false: + quit 1 + else: + if true: + j() + nil # compiles with (ref A)(nil) here + else: + (ref A)(nil) + doAssert x.isNil + + let y = + case true + of false: + (ref A)(nil) + else: + case true + of false: + quit 1 + else: + case true + of true: + j() + nil # compiles with (ref A)(nil) here + else: + (ref A)(nil) + doAssert y.isNil From 57de460437924a951d393ced8b7c88418fe2541a Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Wed, 28 Jun 2023 14:38:54 -0600 Subject: [PATCH 025/347] Don't throw errors on RST tables in Markdown and RstMarkdown modes (#22165) * Don't throw errors on RST tables in Markdown and RstMarkdown modes Additions to RST simple tables (#19859) made their parsing more restrictive, which can introduce problems with of some old nimforum posts, which have tables with sloppily aligned columns (like this one: https://github.com/nim-lang/nimforum/issues/330#issuecomment-1376039966). Also this strictness contradicts to Markdown style of not getting in the way (ignoring errors). So this PR proposes a new strategy of dealing with errors: * In Markdown and legacy (old default) RstMarkdown we try to continue parsing, emitting only warnings * And only in pure RST mode we throw a error I expect that this strategy will be applied to more parts of markup code in the future. * Don't return anything in `checkColumns` --- lib/packages/docutils/rst.nim | 44 +++++++++++++++++++++++++++-------- tests/stdlib/trst.nim | 33 ++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index ae564b6431ad..2894010ef5f0 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -585,6 +585,29 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) = p.col + currentTok(p).col, msgKind, currentTok(p).symbol) +# Functions `isPureRst` & `stopOrWarn` address differences between +# Markdown and RST: +# * Markdown always tries to continue working. If it is really impossible +# to parse a markup element, its proc just returns `nil` and parsing +# continues for it as for normal text paragraph. +# The downside is that real mistakes/typos are often silently ignored. +# The same applies to legacy `RstMarkdown` mode for nimforum. +# * RST really signals errors. The downside is that it's more intrusive - +# the user must escape special syntax with \ explicitly. +# +# TODO: we need to apply this strategy to all markup elements eventually. + +func isPureRst(p: RstParser): bool = + roSupportMarkdown notin p.s.options + +proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string) = + let realMsgKind = if isPureRst(p): errorType else: mwRstStyle + rstMessage(p, realMsgKind, arg) + +proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string, line, col: int) = + let realMsgKind = if isPureRst(p): errorType else: mwRstStyle + rstMessage(p, realMsgKind, arg, line, col) + proc currInd(p: RstParser): int = result = p.indentStack[high(p.indentStack)] @@ -2596,11 +2619,11 @@ proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int = proc checkColumns(p: RstParser, cols: RstCols) = var i = p.idx if p.tok[i].symbol[0] != '=': - rstMessage(p, mwRstStyle, + stopOrWarn(p, meIllformedTable, "only tables with `=` columns specification are allowed") for col in 0 ..< cols.len: if tokEnd(p, i) != cols[col].stop: - rstMessage(p, meIllformedTable, + stopOrWarn(p, meIllformedTable, "end of table column #$1 should end at position $2" % [ $(col+1), $(cols[col].stop+ColRstOffset)], p.tok[i].line, tokEnd(p, i)) @@ -2609,12 +2632,12 @@ proc checkColumns(p: RstParser, cols: RstCols) = if p.tok[i].kind == tkWhite: inc i if p.tok[i].kind notin {tkIndent, tkEof}: - rstMessage(p, meIllformedTable, "extraneous column specification") + stopOrWarn(p, meIllformedTable, "extraneous column specification") elif p.tok[i].kind == tkWhite: inc i else: - rstMessage(p, meIllformedTable, "no enough table columns", - p.tok[i].line, p.tok[i].col) + stopOrWarn(p, meIllformedTable, + "no enough table columns", p.tok[i].line, p.tok[i].col) proc getSpans(p: RstParser, nextLine: int, cols: RstCols, unitedCols: RstCols): seq[int] = @@ -2669,17 +2692,18 @@ proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNo if tokEnd(p) <= colEnd(nCell): if tokStart(p) < colStart(nCell): if currentTok(p).kind != tkWhite: - rstMessage(p, meIllformedTable, + stopOrWarn(p, meIllformedTable, "this word crosses table column from the left") - else: - inc p.idx + row[nCell].add(currentTok(p).symbol) else: row[nCell].add(currentTok(p).symbol) - inc p.idx + inc p.idx else: if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite: - rstMessage(p, meIllformedTable, + stopOrWarn(p, meIllformedTable, "this word crosses table column from the right") + row[nCell].add(currentTok(p).symbol) + inc p.idx inc nCell if currentTok(p).kind == tkIndent: inc p.idx if tokEnd(p) <= colEnd(0): break diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 5da5fc360aeb..329adc101eb3 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -29,7 +29,9 @@ import os import std/[assertions, syncio] const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} +# legacy nimforum / old default mode: const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled} +const pureRst = {roNimFile, roSandboxDisabled} proc toAst(input: string, rstOptions: RstParseOptions = preferMarkdown, @@ -917,10 +919,31 @@ suite "RST tables": ====== ====== Inputs Output ====== ====== - """.toAst(error=error) == "") + """.toAst(rstOptions = pureRst, error = error) == "") check(error[] == "input(2, 2) Error: Illformed table: " & "this word crosses table column from the right") + # In nimforum compatibility mode & Markdown we raise a warning instead: + let expected = dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnLeaf 'Inputs' + rnTableDataCell + rnLeaf 'Output' + """ + for opt in [preferRst, preferMarkdown]: + var warnings = new seq[string] + + check( + dedent""" + ====== ====== + Inputs Output + ====== ====== + """.toAst(rstOptions = opt, warnings = warnings) == expected) + check(warnings[] == @[ + "input(2, 2) Warning: RST style: this word crosses table column from the right"]) + test "tables with slightly overflowed cells cause an error (2)": var error = new string check("" == dedent""" @@ -929,7 +952,7 @@ suite "RST tables": ===== ===== ====== False False False ===== ===== ====== - """.toAst(error=error)) + """.toAst(rstOptions = pureRst, error = error)) check(error[] == "input(2, 8) Error: Illformed table: " & "this word crosses table column from the right") @@ -941,7 +964,7 @@ suite "RST tables": ===== ===== ====== False False False ===== ===== ====== - """.toAst(error=error)) + """.toAst(rstOptions = pureRst, error = error)) check(error[] == "input(2, 7) Error: Illformed table: " & "this word crosses table column from the left") @@ -954,7 +977,7 @@ suite "RST tables": ===== ====== False False ===== ======= - """.toAst(error=error)) + """.toAst(rstOptions = pureRst, error = error)) check(error[] == "input(5, 14) Error: Illformed table: " & "end of table column #2 should end at position 13") @@ -966,7 +989,7 @@ suite "RST tables": ===== ======= False False ===== ====== - """.toAst(error=error)) + """.toAst(rstOptions = pureRst, error = error)) check(error[] == "input(3, 14) Error: Illformed table: " & "end of table column #2 should end at position 13") From d139d99946c97dca9c864709726841855a089496 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:51:18 +0800 Subject: [PATCH 026/347] fixes #19101; zero initialization union casts (#22185) * zero initialization union casts * cleans up and adds a test case for #19101 * uses nimZeroMem --- compiler/ccgexprs.nim | 11 +++++++++-- compiler/condsyms.nim | 2 +- compiler/lineinfos.nim | 4 ++-- compiler/nim.cfg | 3 --- compiler/semexprs.nim | 5 ----- tests/stdlib/tcasts.nim | 26 ++++++++++++++++++++++++++ 6 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 tests/stdlib/tcasts.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 978ca8db8fe6..e2498b57b4c7 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2205,8 +2205,15 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = var lbl = p.labels.rope var tmp: TLoc tmp.r = "LOC$1.source" % [lbl] - linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", - [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl]) + let destsize = getSize(p.config, destt) + let srcsize = getSize(p.config, srct) + + if destsize > srcsize: + linefmt(p, cpsLocals, "union { $1 dest; $2 source; } LOC$3;$n #nimZeroMem(&LOC$3, sizeof(LOC$3));$n", + [getTypeDesc(p.module, e.typ), getTypeDesc(p.module, e[1].typ), lbl]) + else: + linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", + [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl]) tmp.k = locExpr tmp.lode = lodeTyp srct tmp.storage = OnStack diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 4ff588852d7d..12634248c397 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -145,7 +145,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasCallsitePragma") defineSymbol("nimHasAmbiguousEnumHint") - defineSymbol("nimHasWarnCastSizes") + defineSymbol("nimHasWarnCastSizes") # deadcode defineSymbol("nimHasOutParams") defineSymbol("nimHasSystemRaisesDefect") defineSymbol("nimHasWarnUnnamedBreak") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 7c5a99c79bff..37adc5660ee2 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -83,7 +83,7 @@ type warnCstringConv = "CStringConv", warnPtrToCstringConv = "PtrToCstringConv", warnEffect = "Effect", - warnCastSizes = "CastSizes" + warnCastSizes = "CastSizes", # deadcode warnAboveMaxSizeSet = "AboveMaxSizeSet", warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition", warnUnnamedBreak = "UnnamedBreak", @@ -185,7 +185,7 @@ const warnCstringConv: "$1", warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; this will become a compile time error in the future", warnEffect: "$1", - warnCastSizes: "$1", + warnCastSizes: "$1", # deadcode warnAboveMaxSizeSet: "$1", warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma", warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead", diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 5b418cfd3b2d..c32dba4d13a1 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -31,9 +31,6 @@ define:useStdoutAsStdmsg warning[ObservableStores]:off @end -@if nimHasWarnCastSizes: - warning[CastSizes]:on -@end @if nimHasWarningAsError: warningAsError[GcUnsafe2]:on diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3b40425cf47f..1d917f00d9f3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -288,11 +288,6 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = result = (dstSize >= srcSize) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) - if result and (dstSize > srcSize): - var warnMsg = "target type is larger than source type" - warnMsg.add("\n target type: '$1' ($2)" % [$dst, if dstSize == 1: "1 byte" else: $dstSize & " bytes"]) - warnMsg.add("\n source type: '$1' ($2)" % [$src, if srcSize == 1: "1 byte" else: $srcSize & " bytes"]) - message(conf, info, warnCastSizes, warnMsg) if result and src.kind == tyNil: return dst.size <= conf.target.ptrSize diff --git a/tests/stdlib/tcasts.nim b/tests/stdlib/tcasts.nim new file mode 100644 index 000000000000..e01c7e9402b9 --- /dev/null +++ b/tests/stdlib/tcasts.nim @@ -0,0 +1,26 @@ +import std/[strutils] +import std/[assertions, objectdollar] + +# bug #19101 +type + Small = object + a: int + + Big = object + a, b, c, d: int + +proc main = + var + n = 1'i8 + f = 2.0 + s = Small(a: 1) + b = Big(a: 12345, b: 23456, c: 34567, d: 45678) + + doAssert $cast[int](f).toBin(64) == "0100000000000000000000000000000000000000000000000000000000000000" + f = cast[float](n) + doAssert $cast[int](f).toBin(64) == "0000000000000000000000000000000000000000000000000000000000000001" + + doAssert $b == "(a: 12345, b: 23456, c: 34567, d: 45678)" + b = cast[Big](s) + doAssert $b == "(a: 1, b: 0, c: 0, d: 0)" +main() From 41ec894cb0aa0b0d233a0f62ff4929d64ddad3a7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:21:22 +0800 Subject: [PATCH 027/347] alternative to #22183; nimscript shares the same compileTime sym with VM (#22184) --- compiler/vmgen.nim | 6 +++++- tests/vm/mscriptcompiletime.nim | 7 +++++++ tests/vm/tscriptcompiletime.nims | 9 +++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/vm/mscriptcompiletime.nim create mode 100644 tests/vm/tscriptcompiletime.nims diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 067965469ca1..50331b9710e1 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1911,6 +1911,8 @@ proc genVarSection(c: PCtx; n: PNode) = let s = a[0].sym checkCanEval(c, a[0]) if s.isGlobal: + let runtimeAccessToCompileTime = c.mode == emRepl and + sfCompileTime in s.flags and s.position > 0 if s.position == 0: if importcCond(c, s): c.importcSym(a.info, s) else: @@ -1920,7 +1922,9 @@ proc genVarSection(c: PCtx; n: PNode) = assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len - if a[2].kind != nkEmpty: + if runtimeAccessToCompileTime: + discard + elif a[2].kind != nkEmpty: let tmp = c.genx(a[0], {gfNodeAddr}) let val = c.genx(a[2]) c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val) diff --git a/tests/vm/mscriptcompiletime.nim b/tests/vm/mscriptcompiletime.nim new file mode 100644 index 000000000000..ed7e7c029a2f --- /dev/null +++ b/tests/vm/mscriptcompiletime.nim @@ -0,0 +1,7 @@ +# bug.nim +var bar* {.compileTime.} = 1 + +proc dummy = discard + +static: + inc bar \ No newline at end of file diff --git a/tests/vm/tscriptcompiletime.nims b/tests/vm/tscriptcompiletime.nims new file mode 100644 index 000000000000..daec54bf7a15 --- /dev/null +++ b/tests/vm/tscriptcompiletime.nims @@ -0,0 +1,9 @@ +discard """ + cmd: "nim e $file" +""" + +import mscriptcompiletime + +macro foo = + doAssert bar == 2 +foo() From 210b10dd0d47c8c79b686a69eb8646df869cf207 Mon Sep 17 00:00:00 2001 From: metagn Date: Thu, 29 Jun 2023 23:05:18 +0300 Subject: [PATCH 028/347] fix nested call regression in generic bodies (#22189) fixes #22187 --- compiler/sigmatch.nim | 11 +++++++++++ tests/statictypes/tgenericcomputedrange.nim | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f94b4ee60e2b..b2f52ba120ef 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1140,6 +1140,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric + + of tyFromExpr: + if c.c.inGenericContext > 0: + # generic type bodies can sometimes compile call expressions + # prevent expressions with unresolved types from + # being passed as parameters + return isNone else: discard case f.kind @@ -2215,6 +2222,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, of isNone: # do not do this in ``typeRel`` as it then can't infer T in ``ref T``: if a.kind in {tyProxy, tyUnknown}: + if a.kind == tyUnknown and c.inGenericContext > 0: + # don't bother with fauxMatch mechanism in generic type, + # reject match, typechecking will be delayed to instantiation + return nil inc(m.genericMatches) m.fauxMatch = a.kind return arg diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim index 01ec4767a671..82abe267701a 100644 --- a/tests/statictypes/tgenericcomputedrange.nim +++ b/tests/statictypes/tgenericcomputedrange.nim @@ -95,3 +95,23 @@ block: x[2] = 3 doAssert x == [0: 1, 1: 2, 2: 3] doAssert x is array[0 .. 2, int] + +block: + type Foo[T; U: static T] = array[T(0) .. (U * 2) + 1, int] + + block: + var x: Foo[int, 1] + x[0] = 1 + x[1] = 2 + x[2] = 3 + x[3] = 4 + doAssert x == [0: 1, 1: 2, 2: 3, 3: 4] + doAssert x is array[0 .. 3, int] + +block: # issue #22187 + template m(T: type, s: int64): int64 = s + func p(n: int64): int = int(n) + type F[T; s: static int64] = object + k: array[p(m(T, s)), int64] + var x: F[int, 3] + doAssert x.k is array[3, int64] From 3a1adf7d66261c081b5fad9993cc4866728d3a6d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 30 Jun 2023 14:54:29 +0200 Subject: [PATCH 029/347] v2.0: changelog improvements (#22192) * changelog improvements * changelog that people want to actually read * improvements --- changelogs/changelog_2_0_0.md | 747 +++++++++----------------- changelogs/changelog_2_0_0_details.md | 545 +++++++++++++++++++ 2 files changed, 810 insertions(+), 482 deletions(-) create mode 100644 changelogs/changelog_2_0_0_details.md diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index f82cfe7cc2aa..85a0753d0045 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -1,548 +1,331 @@ -# v2.0.0 - yyyy-mm-dd +# v2.0.0 - 2023-07-dd +Version 2.0 is a big milestone with too many changes to list them all here. -## Changes affecting backward compatibility -- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`. +For a full list see [details](changelog_2_0_0_details.html) -- `addr` is now available for all addressable locations, - `unsafeAddr` is now deprecated and an alias for `addr`. -- Certain definitions from the default `system` module have been moved to - the following new modules: +## New features - - `std/syncio` - - `std/assertions` - - `std/formatfloat` - - `std/objectdollar` - - `std/widestrs` - - `std/typedthreads` - - `std/sysatomics` +### Better tuple unpacking - In the future, these definitions will be removed from the `system` module, - and their respective modules will have to be imported to use them. - Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option - may be used. +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. -- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated - symbols in the `system` module: - - Aliases with `Error` suffix to exception types that have a `Defect` suffix - (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)): - `ArithmeticError`, `DivByZeroError`, `OverflowError`, - `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, - `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, - `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, - `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, - `DeadThreadError`, `NilAccessError` - - `addQuitProc`, replaced by `exitprocs.addExitProc` - - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` - - `TaintedString`, formerly a distinct alias to `string` - - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to - `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` +```nim +proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3) -- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in - in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command "nimble install threading" and import `threading/channels`. +# Now nesting is supported! +let (x, (_, y), _, z) = returnsNestedTuple() -- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. +``` -- Enabling `-d:nimPreviewProcConversion`, `proc` does not support conversion to - `pointer`. `cast` may be used instead. +### Improved type inference -- The `gc:v2` option is removed. +A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases. -- The `mainmodule` and `m` options are removed. +For example, code like the following now compiles: -- The `threads:on` option is now the default. +```nim +let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] +``` -- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via - `experimental:flexibleOptionalParams`. -- Automatic dereferencing (experimental feature) is removed. +### Forbidden Tags -- The `Math.trunc` polyfill for targeting Internet Explorer was - previously included in most JavaScript output files. - Now, it is only included with `-d:nimJsMathTruncPolyfill`. - If you are targeting Internet Explorer, you may choose to enable this option - or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). - Nim uses `Math.trunc` for the division and modulo operators for integers. +[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition +of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types. -- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and -`sink` for optimization purposes. +For example: -- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. +```nim -- The `{.this.}` pragma, deprecated since 0.19, has been removed. -- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. - ```nim - type Foo = distinct ptr int +type IO = object ## input/output effect +proc readLine(): string {.tags: [IO].} = discard +proc echoLine(): void = discard - # Before: - var x: Foo = nil - # After: - var x: Foo = Foo(nil) - ``` -- Removed two type pragma syntaxes deprecated since 0.20, namely - `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. +proc no_IO_please() {.forbids: [IO].} = + # this is OK because it didn't define any tag: + echoLine() + # the compiler prevents this: + let y = readLine() -- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent - with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. - This decision was made with the assumption that the old syntax was used rarely; - if your code used the old syntax, please be aware of this change. +``` -- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators - are no longer experimental. +### New standard libraries -- Removed the `nimIncrSeqV3` define. +The famous `os` module got an overhaul. Several of its features are available +under a new interface that introduces a `Path` abstraction. A `Path` is +a `distinct string` and improves the type safety when dealing with paths, files +and directories. -- `macros.getImpl` for `const` symbols now returns the full definition node - (as `nnkConstDef`) rather than the AST of the constant value. +Use: -- Lock levels are deprecated, now a noop. +- `std/oserrors` for OS error reporting. +- `std/envvars` for environment variables handling. +- `std/paths` for path handling. +- `std/dirs` for directory creation/deletion/traversal. +- `std/files` for file existence checking, file deletions and moves. +- `std/symlinks` for symlink handling. +- `std/appdirs` for accessing configuration/home/temp directories. +- `std/cmdline` for reading command line parameters. -- ORC is now the default memory management strategy. Use - `--mm:refc` for a transition period. -- `strictEffects` are no longer experimental. - Use `legacy:laxEffects` to keep backward compatibility. +### Consistent underscore handling -- The `gorge`/`staticExec` calls will now return a descriptive message in the output - if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. +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: -- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. - This warning will become an error in future versions! Use a `cast` operation - like `cast[cstring](x)` instead. +```nim +proc foo(_: int): int = _ + 1 +echo foo(1) -- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. +proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] +echo foo[int]() -- Redefining templates with the same signature was previously - allowed to support certain macro code. To do this explicitly, the - `{.redefine.}` pragma has been added. Note that this is only for templates. - Implicit redefinition of templates is now deprecated and will give an error in the future. +proc _() = echo "_" +_() -- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. +type _ = int +let x: _ = 3 +``` -- Several Standard libraries are moved to nimble packages, use `nimble` to install them: - - `std/punycode` => `punycode` - - `std/asyncftpclient` => `asyncftpclient` - - `std/smtp` => `smtp` - - `std/db_common` => `db_connector/db_common` - - `std/db_sqlite` => `db_connector/db_sqlite` - - `std/db_mysql` => `db_connector/db_mysql` - - `std/db_postgres` => `db_connector/db_postgres` - - `std/db_odbc` => `db_connector/db_odbc` - - `std/md5` => `checksums/md5` - - `std/sha1` => `checksums/sha1` +Whereas the following code now compiles: -- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of - `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. - This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. +```nim +proc foo(_, _: int): int = 123 +echo foo(1, 2) -- When `--warning[BareExcept]:on` is enabled, if no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. +proc foo[_, _](): int = 123 +echo foo[int, bool]() -- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. +proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U)) +echo foo(int, bool) -- 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: +proc _() = echo "one" +proc _() = echo "two" - ```nim - proc foo(_: int): int = _ + 1 - echo foo(1) +type _ = int +type _ = float +``` - proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] - echo foo[int]() +### JavaScript codegen improvement - proc _() = echo "_" - _() +The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) +for 64-bit integer types (`int64` and `uint64`) by default. As this affects +JS code generation, code using these types to interface with the JS backend +may need to be updated. Note that `int` and `uint` are not affected. - type _ = int - let x: _ = 3 - ``` +For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint) +and in the case of potential bugs with the new implementation, the +old behavior is currently still supported with the command line option +`--jsbigint64:off`. - Whereas the following code now compiles: - ```nim - proc foo(_, _: int): int = 123 - echo foo(1, 2) +## Docgen improvements - proc foo[_, _](): int = 123 - echo foo[int, bool]() +`Markdown` is now default markup language of doc comments (instead +of legacy `RstMarkdown` mode). In this release we begin to separate +RST and Markdown features to better follow specification of each +language, with the focus on Markdown development. - proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U)) - echo foo(int, bool) +* So we added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to + select the markup language mode in the doc comments of current `.nim` + file for processing by `nim doc`: - proc _() = echo "one" - proc _() = echo "two" + 1. `Markdown` (default) is basically CommonMark (standard Markdown) + + some Pandoc Markdown features + some RST features that are missing + in our current implementation of CommonMark and Pandoc Markdown. + 2. `RST` closely follows RST spec with few additional Nim features. + 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which + is kept for the sake of compatibility and ease of migration. - type _ = int - type _ = float - ``` - -- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. - -- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) - for 64-bit integer types (`int64` and `uint64`) by default. As this affects - JS code generation, code using these types to interface with the JS backend - may need to be updated. Note that `int` and `uint` are not affected. +* We added separate `md2html` and `rst2html` commands for processing + standalone `.md` and `.rst` files respectively (and also `md2tex/rst2tex`). - For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint) - and in the case of potential bugs with the new implementation, the - old behavior is currently still supported with the command line option - `--jsbigint64:off`. - -- The `proc` and `iterator` type classes now respectively only match - procs and iterators. Previously both type classes matched any of - procs or iterators. - - ```nim - proc prc(): int = - 123 - - iterator iter(): int = - yield 123 - - proc takesProc[T: proc](x: T) = discard - proc takesIter[T: iterator](x: T) = discard - - # always compiled: - takesProc(prc) - takesIter(iter) - # no longer compiles: - takesProc(iter) - takesIter(prc) - ``` - -- The `proc` and `iterator` type classes now accept a calling convention pragma - (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator - types. Previously pragmas were parsed but discarded if no parameter list - was given. - - This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with - an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma - node in the same place as in a concrete `proc` or `iterator` type node. This - state of the AST may be unexpected to existing code, both due to the - replacement of the `nnkFormalParams` node as well as having child nodes - unlike other type class AST. - -- Signed integer literals in `set` literals now default to a range type of - `0..255` instead of `0..65535` (the maximum size of sets). - -- Case statements with else branches put before elif/of branches in macros - are rejected with "invalid order of case branches". - -- Destructors now default to `.raises: []` (i.e. destructors must not raise - unlisted exceptions) and explicitly raising destructors are implementation - defined behavior. - -- The very old, undocumented deprecated pragma statement syntax for - deprecated aliases is now a no-op. The regular deprecated pragma syntax is - generally sufficient instead. - - ```nim - # now does nothing: - {.deprecated: [OldName: NewName].} - - # instead use: - type OldName* {.deprecated: "use NewName instead".} = NewName - const oldName* {.deprecated: "use newName instead".} = newName - ``` - - `defined(nimalias)` can be used to check for versions when this syntax was - available; however since code that used this syntax is usually very old, - these deprecated aliases are likely not used anymore and it may make sense - to simply remove these statements. - -- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer - declared when they are not available on the backend. Previously it would call - `doAssert false` at runtime despite the condition being compile-time. - -- Custom destructors now supports non-var parameters, e.g. `proc =destroy[T: object](x: T)` is valid. `proc =destroy[T: object](x: var T)` is deprecated. - -- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly. - -## Standard library additions and changes - -[//]: # "Changes:" -- OpenSSL 3 is now supported. -- `macros.parseExpr` and `macros.parseStmt` now accept an optional - filename argument for more informative errors. -- Module `colors` expanded with missing colors from the CSS color standard. - `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. -- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). -- The `md5` module now works at compile time and in JavaScript. -- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. -- `strutils.find` now uses and defaults to `last = -1` for whole string searches, - making limiting it to just the first char (`last = 0`) valid. -- `strutils.split` and `strutils.rsplit` now return a source string as a single element for an empty separator. -- `random.rand` now works with `Ordinal`s. -- Undeprecated `os.isvalidfilename`. -- `std/oids` now uses `int64` to store time internally (before it was int32). -- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. -- `std/uri.Uri.isIpv6` is now exported. -- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. - - -- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`. -- `std/jsfetch.newFetchOptions` now has default values for all parameters -- `std/jsformdata` now accepts `Blob` data type. - -- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). - -- New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall. - - This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. - - On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. - - When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: - ```sh - $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details - $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 - $ sh build.sh # per unix install instructions - $ bin/nim c koch # per unix install instructions - $ ./koch boot -d:release # per unix install instructions - $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall - ``` - - This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular). - -[//]: # "Additions:" -- Added ISO 8601 week date utilities in `times`: - - Added `IsoWeekRange`, a range type for weeks in a week-based year. - - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. - - Added a `initDateTime` overload to create a datetime from an ISO week date. - - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. - - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. -- Added new modules which were part of `std/os`: - - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. - - Added `std/cmdline` for reading command line parameters. -- Added `sep` parameter in `std/uri` to specify the query separator. -- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. -- Added `complex.sgn` for obtaining the phase of complex numbers. -- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, - `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, - `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, - `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, - `toggleAttribute`, and `matches` to `std/dom`. -- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) -- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 -- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse. -- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse. -- Added `safe` parameter to `base64.encodeMime`. -- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. -- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences. -- `std/jscore` for JavaScript targets: - + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) - and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask). - + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`. -- Added `BackwardsIndex` overload for `CacheSeq`. -- Added support for nested `with` blocks in `std/with`. - - -[//]: # "Deprecations:" -- Deprecated `selfExe` for Nimscript. -- Deprecated `std/sums`. -- Deprecated `std/base64.encode` for collections of arbitrary integer element type. - Now only `byte` and `char` are supported. - -[//]: # "Removals:" -- Removed deprecated module `parseopt2`. -- Removed deprecated module `sharedstrings`. -- Removed deprecated module `dom_extensions`. -- Removed deprecated module `LockFreeHash`. -- Removed deprecated module `events`. -- Removed deprecated `oids.oidToString`. -- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. -- Removed deprecated `jsre.test` and `jsre.toString`. -- Removed deprecated `math.c_frexp`. -- Removed deprecated `` httpcore.`==` ``. -- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. -- Removed deprecated `osproc.poDemon`, symbol with typo. -- Removed deprecated `tables.rightSize`. - - -- Removed deprecated `posix.CLONE_STOPPED`. - - -## Language changes - -- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma - which can be used to disable certain effects in proc types. -- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, - meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. -- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed - for the right-hand side of type definitions in type sections. Previously - they would error with "invalid indentation". - -- Compile-time define changes: - - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. - In the command line, this is defined as `-d:a.b.c`. Older versions can - use accents as in ``defined(`a.b.c`)`` to access such defines. - - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) - now support a string argument for qualified define names. - - ```nim - # -d:package.FooBar=42 - const FooBar {.intdefine: "package.FooBar".}: int = 5 - echo FooBar # 42 - ``` - - This was added to help disambiguate similar define names for different packages. - In older versions, this could only be achieved with something like the following: - - ```nim - const FooBar = block: - const `package.FooBar` {.intdefine.}: int = 5 - `package.FooBar` - ``` - - A generic `define` pragma for constants has been added that interprets - the value of the define based on the type of the constant value. - See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma) - for a list of supported types. - -- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: - - Templates now accept macro pragmas. - - Macro pragmas for var/let/const sections have been redesigned in a way that works - similarly to routine macro pragmas. The new behavior is documented in the - [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). - - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, - allowing multiple type definitions to be injected in place of the original type definition. - - ```nim - import macros - macro multiply(amount: static int, s: untyped): untyped = - let name = $s[0].basename - result = newNimNode(nnkTypeSection) - for i in 1 .. amount: - result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) - type - Foo = object - Bar {.multiply: 3.} = object - x, y, z: int - Baz = object - # becomes - type - Foo = object - Bar1 = object - x, y, z: int - Bar2 = object - x, y, z: int - Bar3 = object - x, y, z: int - Baz = object - ``` - -- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) - has been implemented for a variety of basic cases. For example, code like the following now compiles: - - ```nim - let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] - ``` - -- `cstring` is now accepted as a selector in `case` statements, removing the - need to convert to `string`. On the JS backend, this is translated directly - to a `switch` statement. - -- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). -- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. - -- IBM Z architecture and macOS m1 arm64 architecture are supported. - -- `=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. - -- C++ interoperability: - - New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added. - - Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma. - -## Compiler changes - -- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the - reality better. (Nim moved away from all techniques based on "tracing".) - -- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. - -- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, - without requiring `-d:nimVersion140` which is now a noop. - -- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. - -- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and - related functions produced on the backend. This prevents conflicts with other Nim - static libraries. - -- When compiling for Release the flag `-fno-math-errno` is used for GCC. -- Removed deprecated `LineTooLong` hint. -- Line numbers and filenames of source files work correctly inside templates for JavaScript targets. - -- Removed support for LCC (Local C), Pelles C, Digital Mars, Watcom compilers. - - -## Docgen - -- `Markdown` is now default markup language of doc comments (instead - of legacy `RstMarkdown` mode). In this release we begin to separate - RST and Markdown features to better follow specification of each - language, with the focus on Markdown development. - - * So we add `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to - select the markup language mode in the doc comments of current `.nim` - file for processing by `nim doc`: - - 1. `Markdown` (default) is basically CommonMark (standard Markdown) + - some Pandoc Markdown features + some RST features that are missing - in our current implementation of CommonMark and Pandoc Markdown. - 2. `RST` closely follows RST spec with few additional Nim features. - 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which - is kept for the sake of compatibility and ease of migration. - - * and we add separate `md2html` and `rst2html` commands for processing - standalone `.md` and `.rst` files respectively (and also `md2tex/rst2tex`). - -- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. -- Docgen now supports concise syntax for referencing Nim symbols: +* We added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. +* The docgen now supports concise syntax for referencing Nim symbols: instead of specifying HTML anchors directly one can use original Nim symbol declarations (adding the aforementioned link brackets `[...]` around them). - * to use this feature across modules a new `importdoc` directive is added. +* To use this feature across modules a new `importdoc` directive was added. Using this feature for referencing also helps to ensure that links (inside one module or the whole project) are not broken. -- Added support for RST & Markdown quote blocks (blocks starting from `>`). -- Added a popular Markdown definition lists extension. -- Added Markdown indented code blocks (blocks indented by >= 4 spaces). -- Added syntax for additional parameters to Markdown code blocks: +* We added support for RST & Markdown quote blocks (blocks starting from `>`). +* We added a popular Markdown definition lists extension. +* Markdown indented code blocks (blocks indented by >= 4 spaces) have been added. +* We added syntax for additional parameters to Markdown code blocks: ```nim test="nim c $1" ... ``` +## C++ interop enhancements + +Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma +Now one can define constructors and virtual that maps to C++ constructors and virtual methods. Allowing one to further customize +the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so it works on types. + +It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces. + +Consider the following example: + +```cpp + +struct Base { + int someValue; + Base(int inValue) { + someValue = inValue; + }; +}; +class IPrinter { +public: + virtual void print() = 0; +}; +``` + +```nim + +type + Base* {.importcpp, inheritable.} = object + someValue*: int32 + IPrinter* {.importcpp.} = object + +const objTemplate = """ + struct $1 : public $3, public IPrinter { + $2 + }; +"""; + +type NimChild {.codegenDecl:objTemplate .} = object of Base + +proc makeNimChild(val: int32): NimChild {.constructor:"NimClass('1 #1) : Base(#1)".} = + echo "It calls the base constructor passing " & $this.someValue + this.someValue = val * 2 #notice how we can access to this inside the constructor. it's of the type ptr NimChild + +proc print*(self: NimChild) {.virtual.} = + echo "Some value is " & $self.someValue + +let child = makeNimChild(10) +child.print() +``` + +It outputs: + +``` +It calls the base constructor passing 10 +Some value is 20 +``` + + +## ARC/ORC refinements + +With the release 2.0 the ARC/ORC model got refined once again and is now finally complete: + +1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable. +2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations. +3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter. + +With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what? +Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference. + +But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of +providing a stable ABI it is important not to lose any efficiency in the calling conventions. + + ## Tool changes - Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. -- nimgrep added option `--inContext` (and `--notInContext`), which +- nimgrep now offers the option `--inContext` (and `--notInContext`), which allows to filter only matches with context block containing a given pattern. - nimgrep: names of options containing "include/exclude" are deprecated, e.g. instead of `--includeFile` and `--excludeFile` we have `--filename` and `--notFilename` respectively. Also the semantics become consistent for such positive/negative filters. -- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice. +- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions. + + +## Porting guide + +### Block and Break + +Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn: + +```nim + +block: + a() + if cond: + break + b() + +``` + +Into: + +```nim + +block maybePerformB: + a() + if cond: + break maybePerformB + b() + +``` + +### Strict funcs + +The definition of "strictFuncs" was changed. +The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter". +The new definition is: "A store to a ref/ptr deref is forbidden." + +This new definition is much easier to understand, the price is some expressitivity. The following code used to be +accepted: + +```nim + +{.experimental: "strictFuncs".} + +type Node = ref object + s: string + +func create(s: string): Node = + result = Node() + result.s = s # store to result[] + +``` + +Now it has to be rewritten to: + +```nim + +{.experimental: "strictFuncs".} + +type Node = ref object + s: string + +func create(s: string): Node = + result = Node(s: s) + +``` + + + +### Standard library + +Several Standard libraries have been moved to nimble packages, use `nimble` or `atlas` to install them: + +- `std/punycode` => `punycode` +- `std/asyncftpclient` => `asyncftpclient` +- `std/smtp` => `smtp` +- `std/db_common` => `db_connector/db_common` +- `std/db_sqlite` => `db_connector/db_sqlite` +- `std/db_mysql` => `db_connector/db_mysql` +- `std/db_postgres` => `db_connector/db_postgres` +- `std/db_odbc` => `db_connector/db_odbc` +- `std/md5` => `checksums/md5` +- `std/sha1` => `checksums/sha1` + diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md new file mode 100644 index 000000000000..0cdf3d326be7 --- /dev/null +++ b/changelogs/changelog_2_0_0_details.md @@ -0,0 +1,545 @@ +# v2.0.0 - yyyy-mm-dd + + +## Changes affecting backward compatibility +- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behaviors. Previously it raised `ValueError`. + +- `addr` is now available for all addressable locations, + `unsafeAddr` is now deprecated and an alias for `addr`. + +- Certain definitions from the default `system` module have been moved to + the following new modules: + + - `std/syncio` + - `std/assertions` + - `std/formatfloat` + - `std/objectdollar` + - `std/widestrs` + - `std/typedthreads` + - `std/sysatomics` + + In the future, these definitions will be removed from the `system` module, + and their respective modules will have to be imported to use them. + Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option + may be used. + +- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated + symbols in the `system` module: + - Aliases with `Error` suffix to exception types that have a `Defect` suffix + (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)): + `ArithmeticError`, `DivByZeroError`, `OverflowError`, + `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, + `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, + `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, + `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, + `DeadThreadError`, `NilAccessError` + - `addQuitProc`, replaced by `exitprocs.addExitProc` + - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` + - `TaintedString`, formerly a distinct alias to `string` + - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to + `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` + +- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in + in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command "nimble install threading" and import `threading/channels`. + +- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. + +- Enabling `-d:nimPreviewProcConversion`, `proc` does not support conversion to + `pointer`. `cast` may be used instead. + +- The `gc:v2` option is removed. + +- The `mainmodule` and `m` options are removed. + +- The `threads:on` option is now the default. + +- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via + `experimental:flexibleOptionalParams`. + +- Automatic dereferencing (experimental feature) is removed. + +- The `Math.trunc` polyfill for targeting Internet Explorer was + previously included in most JavaScript output files. + Now, it is only included with `-d:nimJsMathTruncPolyfill`. + If you are targeting Internet Explorer, you may choose to enable this option + or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). + Nim uses `Math.trunc` for the division and modulo operators for integers. + +- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and +`sink` for optimization purposes. + +- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. + +- The `{.this.}` pragma, deprecated since 0.19, has been removed. +- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. + ```nim + type Foo = distinct ptr int + + # Before: + var x: Foo = nil + # After: + var x: Foo = Foo(nil) + ``` +- Removed two type pragma syntaxes deprecated since 0.20, namely + `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. + +- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent + with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. + This decision was made with the assumption that the old syntax was used rarely; + if your code used the old syntax, please be aware of this change. + +- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators + are no longer experimental. + +- `macros.getImpl` for `const` symbols now returns the full definition node + (as `nnkConstDef`) rather than the AST of the constant value. + +- Lock levels are deprecated, now a noop. + +- ORC is now the default memory management strategy. Use + `--mm:refc` for a transition period. + +- `strictEffects` are no longer experimental. + Use `legacy:laxEffects` to keep backward compatibility. + +- The `gorge`/`staticExec` calls will now return a descriptive message in the output + if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. + +- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. + This warning will become an error in future versions! Use a `cast` operation + like `cast[cstring](x)` instead. + +- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. + +- Redefining templates with the same signature was previously + allowed to support certain macro code. To do this explicitly, the + `{.redefine.}` pragma has been added. Note that this is only for templates. + Implicit redefinition of templates is now deprecated and will give an error in the future. + +- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. + +- Several Standard libraries have been moved to nimble packages, use `nimble` to install them: + - `std/punycode` => `punycode` + - `std/asyncftpclient` => `asyncftpclient` + - `std/smtp` => `smtp` + - `std/db_common` => `db_connector/db_common` + - `std/db_sqlite` => `db_connector/db_sqlite` + - `std/db_mysql` => `db_connector/db_mysql` + - `std/db_postgres` => `db_connector/db_postgres` + - `std/db_odbc` => `db_connector/db_odbc` + - `std/md5` => `checksums/md5` + - `std/sha1` => `checksums/sha1` + +- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of + `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. + This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. + +- When `--warning[BareExcept]:on` is enabled, if no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. + +- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. + +- 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 + ``` + + Whereas the following code now compiles: + + ```nim + proc foo(_, _: int): int = 123 + echo foo(1, 2) + + 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. + +- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) + for 64-bit integer types (`int64` and `uint64`) by default. As this affects + JS code generation, code using these types to interface with the JS backend + may need to be updated. Note that `int` and `uint` are not affected. + + For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint) + and in the case of potential bugs with the new implementation, the + old behavior is currently still supported with the command line option + `--jsbigint64:off`. + +- The `proc` and `iterator` type classes now respectively only match + procs and iterators. Previously both type classes matched any of + procs or iterators. + + ```nim + proc prc(): int = + 123 + + iterator iter(): int = + yield 123 + + proc takesProc[T: proc](x: T) = discard + proc takesIter[T: iterator](x: T) = discard + + # always compiled: + takesProc(prc) + takesIter(iter) + # no longer compiles: + takesProc(iter) + takesIter(prc) + ``` + +- The `proc` and `iterator` type classes now accept a calling convention pragma + (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator + types. Previously pragmas were parsed but discarded if no parameter list + was given. + + This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with + an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma + node in the same place as in a concrete `proc` or `iterator` type node. This + state of the AST may be unexpected to existing code, both due to the + replacement of the `nnkFormalParams` node as well as having child nodes + unlike other type class AST. + +- Signed integer literals in `set` literals now default to a range type of + `0..255` instead of `0..65535` (the maximum size of sets). + +- Case statements with else branches put before elif/of branches in macros + are rejected with "invalid order of case branches". + +- Destructors now default to `.raises: []` (i.e. destructors must not raise + unlisted exceptions) and explicitly raising destructors are implementation + defined behavior. + +- The very old, undocumented deprecated pragma statement syntax for + deprecated aliases is now a no-op. The regular deprecated pragma syntax is + generally sufficient instead. + + ```nim + # now does nothing: + {.deprecated: [OldName: NewName].} + + # instead use: + type OldName* {.deprecated: "use NewName instead".} = NewName + const oldName* {.deprecated: "use newName instead".} = newName + ``` + + `defined(nimalias)` can be used to check for versions when this syntax was + available; however since code that used this syntax is usually very old, + these deprecated aliases are likely not used anymore and it may make sense + to simply remove these statements. + +- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer + declared when they are not available on the backend. Previously it would call + `doAssert false` at runtime despite the condition being checkable at compile-time. + +- Custom destructors now supports non-var parameters, e.g. `proc =destroy[T: object](x: T)` is valid. `proc =destroy[T: object](x: var T)` is deprecated. + +- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly. + +## Standard library additions and changes + +[//]: # "Changes:" +- OpenSSL 3 is now supported. +- `macros.parseExpr` and `macros.parseStmt` now accept an optional + filename argument for more informative errors. +- Module `colors` expanded with missing colors from the CSS color standard. + `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. +- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). +- The `md5` module now works at compile time and in JavaScript. +- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. +- `strutils.find` now uses and defaults to `last = -1` for whole string searches, + making limiting it to just the first char (`last = 0`) valid. +- `strutils.split` and `strutils.rsplit` now return a source string as a single element for an empty separator. +- `random.rand` now works with `Ordinal`s. +- Undeprecated `os.isvalidfilename`. +- `std/oids` now uses `int64` to store time internally (before it was int32). +- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. +- `std/uri.Uri.isIpv6` is now exported. +- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. + + +- `std/jsfetch.newFetchOptions` now has default values for all parameters. +- `std/jsformdata` now accepts the `Blob` data type. + +- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). + +- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on Linux `getrandom` syscall. + + This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. + + On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. + + When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: + ```sh + $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details + $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 + $ sh build.sh # per unix install instructions + $ bin/nim c koch # per unix install instructions + $ ./koch boot -d:release # per unix install instructions + $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall + ``` + + This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (sysrand in particular). + +[//]: # "Additions:" +- Added ISO 8601 week date utilities in `times`: + - Added `IsoWeekRange`, a range type for weeks in a week-based year. + - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. + - Added a `initDateTime` overload to create a datetime from an ISO week date. + - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. + - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. +- Added new modules which were part of `std/os`: + - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. + - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. + - Added `std/cmdline` for reading command line parameters. +- Added `sep` parameter in `std/uri` to specify the query separator. +- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. +- Added `complex.sgn` for obtaining the phase of complex numbers. +- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, + `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, + `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, + `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, + `toggleAttribute`, and `matches` to `std/dom`. +- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) +- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 +- Added `openArray[char]` overloads for `std/parseutils` allowing for more code reuse. +- Added `openArray[char]` overloads for `std/unicode` allowing for more code reuse. +- Added `safe` parameter to `base64.encodeMime`. +- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. +- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences. +- `std/jscore` for JavaScript targets: + + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) + and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask). + + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`. +- Added `BackwardsIndex` overload for `CacheSeq`. +- Added support for nested `with` blocks in `std/with`. + + +[//]: # "Deprecations:" +- Deprecated `selfExe` for Nimscript. +- Deprecated `std/sums`. +- Deprecated `std/base64.encode` for collections of arbitrary integer element type. + Now only `byte` and `char` are supported. + +[//]: # "Removals:" +- Removed deprecated module `parseopt2`. +- Removed deprecated module `sharedstrings`. +- Removed deprecated module `dom_extensions`. +- Removed deprecated module `LockFreeHash`. +- Removed deprecated module `events`. +- Removed deprecated `oids.oidToString`. +- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. +- Removed deprecated `jsre.test` and `jsre.toString`. +- Removed deprecated `math.c_frexp`. +- Removed deprecated `` httpcore.`==` ``. +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. +- Removed deprecated `osproc.poDemon`, symbol with typo. +- Removed deprecated `tables.rightSize`. + + +- Removed deprecated `posix.CLONE_STOPPED`. + + +## Language changes + +- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma + which can be used to disable certain effects in proc types. +- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. +- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed + for the right-hand side of type definitions in type sections. Previously + they would error with "invalid indentation". + +- Compile-time define changes: + - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. + In the command line, this is defined as `-d:a.b.c`. Older versions can + use accents as in ``defined(`a.b.c`)`` to access such defines. + - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) + now support a string argument for qualified define names. + + ```nim + # -d:package.FooBar=42 + const FooBar {.intdefine: "package.FooBar".}: int = 5 + echo FooBar # 42 + ``` + + This was added to help disambiguate similar define names for different packages. + In older versions, this could only be achieved with something like the following: + + ```nim + const FooBar = block: + const `package.FooBar` {.intdefine.}: int = 5 + `package.FooBar` + ``` + - A generic `define` pragma for constants has been added that interprets + the value of the define based on the type of the constant value. + See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma) + for a list of supported types. + +- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: + - Templates now accept macro pragmas. + - Macro pragmas for var/let/const sections have been redesigned in a way that works + similarly to routine macro pragmas. The new behavior is documented in the + [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). + - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, + allowing multiple type definitions to be injected in place of the original type definition. + + ```nim + import macros + macro multiply(amount: static int, s: untyped): untyped = + let name = $s[0].basename + result = newNimNode(nnkTypeSection) + for i in 1 .. amount: + result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) + type + Foo = object + Bar {.multiply: 3.} = object + x, y, z: int + Baz = object + # becomes + type + Foo = object + Bar1 = object + x, y, z: int + Bar2 = object + x, y, z: int + Bar3 = object + x, y, z: int + Baz = object + ``` + +- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) + has been implemented for a variety of basic cases. For example, code like the following now compiles: + + ```nim + let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] + ``` + +- `cstring` is now accepted as a selector in `case` statements, removing the + need to convert to `string`. On the JS backend, this is translated directly + to a `switch` statement. + +- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). +- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. + +- IBM Z architecture and macOS m1 arm64 architecture are supported. + +- `=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. + +- C++ interoperability: + - New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added. + - Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma. + +## Compiler changes + +- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the + reality better. (Nim moved away from all techniques based on "tracing".) + +- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. + +- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, + without requiring `-d:nimVersion140` which is now a noop. + +- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. + +- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and + related functions produced on the backend. This prevents conflicts with other Nim + static libraries. + +- When compiling for Release the flag `-fno-math-errno` is used for GCC. +- Removed deprecated `LineTooLong` hint. +- Line numbers and filenames of source files work correctly inside templates for JavaScript targets. + +- Removed support for LCC (Local C), Pelles C, Digital Mars, Watcom compilers. + + +## Docgen + +- `Markdown` is now default markup language of doc comments (instead + of legacy `RstMarkdown` mode). In this release we begin to separate + RST and Markdown features to better follow specification of each + language, with the focus on Markdown development. + + * So we add `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to + select the markup language mode in the doc comments of current `.nim` + file for processing by `nim doc`: + + 1. `Markdown` (default) is basically CommonMark (standard Markdown) + + some Pandoc Markdown features + some RST features that are missing + in our current implementation of CommonMark and Pandoc Markdown. + 2. `RST` closely follows RST spec with few additional Nim features. + 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which + is kept for the sake of compatibility and ease of migration. + + * and we add separate `md2html` and `rst2html` commands for processing + standalone `.md` and `.rst` files respectively (and also `md2tex/rst2tex`). + +- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. +- Docgen now supports concise syntax for referencing Nim symbols: + instead of specifying HTML anchors directly one can use original + Nim symbol declarations (adding the aforementioned link brackets + `[...]` around them). + * to use this feature across modules a new `importdoc` directive is added. + Using this feature for referencing also helps to ensure that links + (inside one module or the whole project) are not broken. +- Added support for RST & Markdown quote blocks (blocks starting from `>`). +- Added a popular Markdown definition lists extension. +- Added Markdown indented code blocks (blocks indented by >= 4 spaces). +- Added syntax for additional parameters to Markdown code blocks: + + ```nim test="nim c $1" + ... + ``` + +## Tool changes + +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. +- nimgrep added the option `--inContext` (and `--notInContext`), which + allows to filter only matches with context block containing a given pattern. +- nimgrep: names of options containing "include/exclude" are deprecated, + e.g. instead of `--includeFile` and `--excludeFile` we have + `--filename` and `--notFilename` respectively. + Also the semantics become consistent for such positive/negative filters. +- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice. From 4d2ebbb87703bdca1e20f4d94d92b07d97b5c07e Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Fri, 30 Jun 2023 14:54:46 +0200 Subject: [PATCH 030/347] fix controlflow test (#22194) the function actually returns --- tests/arc/tcontrolflow.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim index 80cc2b187eb8..dbc1159034b7 100644 --- a/tests/arc/tcontrolflow.nim +++ b/tests/arc/tcontrolflow.nim @@ -76,7 +76,7 @@ var c = Control(x: 7) run(c) -proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = +proc sysFatal(exceptn: typedesc, message: string) {.inline.} = var buf = newStringOfCap(200) add(buf, "##") add(buf, message) From 2f109595e982bd68b4f4f2fabf934409947b0ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Fri, 30 Jun 2023 22:03:25 +0100 Subject: [PATCH 031/347] reset macrocache after each script evaluation (#22195) --- compiler/nimeval.nim | 5 ++++- tests/compilerapi/tcompilerapi.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 8e8f4ac7bf1c..e98de7e62b69 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -12,7 +12,7 @@ import ast, modules, condsyms, options, llstream, lineinfos, vm, vmdef, modulegraphs, idents, os, pathutils, - scriptconfig, std/compilesettings + scriptconfig, std/[compilesettings, tables] import pipelines @@ -78,6 +78,9 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = assert i != nil assert i.mainModule != nil, "no main module selected" initStrTables(i.graph, i.mainModule) + i.graph.cacheSeqs.clear() + i.graph.cacheCounters.clear() + i.graph.cacheTables.clear() i.mainModule.ast = nil let s = if scriptStream != nil: scriptStream diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim index 828dcd0d9761..2d6cfa0ca3b5 100644 --- a/tests/compilerapi/tcompilerapi.nim +++ b/tests/compilerapi/tcompilerapi.nim @@ -69,3 +69,30 @@ block error_hook: doAssertRaises(VMQuit): i.evalScript() + +block resetmacrocache: + let std = findNimStdLibCompileTime() + let intr = createInterpreter("script.nim", [std, std / "pure", std / "core"]) + proc evalString(intr: Interpreter; code: string) = + let stream = llStreamOpen(code) + intr.evalScript(stream) + llStreamClose(stream) + let code = """ +import std/[macrocache, macros] +static: + let counter = CacheCounter"valTest" + inc counter + assert counter.value == 1 + + const mySeq = CacheSeq"addTest" + mySeq.add(newLit(5)) + mySeq.add(newLit("hello ic")) + assert mySeq.len == 2 + + const mcTable = CacheTable"subTest" + mcTable["toAdd"] = newStmtList() #would crash if not empty + assert mcTable.len == 1 +""" + intr.evalString(code) + intr.evalString(code) + destroyInterpreter(intr) \ No newline at end of file From 7cb59efd4b3d282db5ddc4e7385ee9f05b53a542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=83=E5=A4=8F=E2=9D=A4v?= Date: Sat, 1 Jul 2023 16:16:59 +0800 Subject: [PATCH 032/347] Tidy contributing.md: format cmd (#22204) --- doc/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/contributing.md b/doc/contributing.md index a472f657a7ea..47e1fa3dd647 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -137,9 +137,9 @@ You can run the tests with which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for -```cmd + ```cmd ./koch tests --failing all -``` + ``` You can also run only a single category of tests. A category is a subdirectory in the ``tests/`` directory. There are a couple of special categories; for a From aedb4c26deb91b1f9e3839475386c8e173e3598e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 1 Jul 2023 13:18:21 -0300 Subject: [PATCH 033/347] Bisect Bugs (#22157) * Add Git bisect like for bug repro code on issue comments against Nim versions >1.0 * Add Git bisect like for bug repro code on issue comments against Nim versions >1.0 * Add Git bisect like for bug repro code on issue comments against Nim versions >1.0 --- .github/workflows/bisects.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/bisects.yml diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml new file mode 100644 index 000000000000..c755ef5a2721 --- /dev/null +++ b/.github/workflows/bisects.yml @@ -0,0 +1,20 @@ +# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901 +name: issue comments bisects +on: + issue_comment: + types: created + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + # nimrun-action requires Nim installed. + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: 'devel' + + - uses: juancarlospaco/nimrun-action@nim + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 83a5865024cf391137fcbd4e5ed195cda969ed88 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 2 Jul 2023 20:57:10 +0200 Subject: [PATCH 034/347] fixes #22200 (#22206) * fixes #22200 * typo --- compiler/varpartitions.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index ad8dd245c902..6598ef508b92 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -824,6 +824,10 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = if vid >= 0: if n[1].kind == nkSym and (c.s[vid].reassignedTo == 0 or c.s[vid].reassignedTo == n[1].sym.id): c.s[vid].reassignedTo = n[1].sym.id + if c.inConditional > 0 and c.inLoop > 0: + # bug #22200: live ranges with loops and conditionals are too + # complex for our current analysis, so we prevent the cursorfication. + c.s[vid].flags.incl isConditionallyReassigned else: markAsReassigned(c, vid) From 57296a51397d65ce4f342d6cbc92a9de2629875c Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 3 Jul 2023 03:04:48 +0800 Subject: [PATCH 035/347] fixes #22197; Distinct ref objects + destructor cause C++ codegen error (#22207) --- compiler/semmagic.nim | 8 ++++++++ tests/destructor/tnonvardestructor.nim | 27 +++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 22c2fb57efbb..e7057053b727 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -609,6 +609,14 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, let op = getAttachedOp(c.graph, t, attachedDestructor) if op != nil: result[0] = newSymNode(op) + + if op.typ != nil and op.typ.len == 2 and op.typ[1].kind != tyVar and + skipAddr(n[1]).typ.kind == tyDistinct: + if n[1].kind == nkSym and n[1].sym.kind == skParam and + n[1].typ.kind == tyVar: + result[1] = genDeref(n[1]) + else: + result[1] = skipAddr(n[1]) of mTrace: result = n let t = n[1].typ.skipTypes(abstractVar) diff --git a/tests/destructor/tnonvardestructor.nim b/tests/destructor/tnonvardestructor.nim index e482ba137dd4..1f59a397839f 100644 --- a/tests/destructor/tnonvardestructor.nim +++ b/tests/destructor/tnonvardestructor.nim @@ -192,4 +192,29 @@ block: doAssert $(x[]) == "()" # bug #11705 - foo() \ No newline at end of file + foo() + +block: # bug #22197 + type + H5IdObj = object + H5Id = ref H5IdObj + + FileID = distinct H5Id + + H5GroupObj = object + file_id: FileID + H5Group = ref H5GroupObj + + ## This would make it work! + #proc `=destroy`*(x: FileID) = `=destroy`(cast[H5Id](x)) + ## If this does not exist, it also works! + proc newFileID(): FileID = FileID(H5Id()) + + proc `=destroy`(grp: var H5GroupObj) = + ## Closes the group and resets all references to nil. + if cast[pointer](grp.fileId) != nil: + `=destroy`(grp.file_id) + + var grp = H5Group() + reset(grp.file_id) + reset(grp) From 1ad618d96cab3f518b77c22432600e6b6bf56fae Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:36:05 +0800 Subject: [PATCH 036/347] clean up the documentation (#22196) --- compiler/readme.md | 2 +- doc/advopt.txt | 2 +- doc/filelist.txt | 6 ++---- doc/manual.md | 8 +++----- doc/mm.md | 2 +- doc/nims.md | 2 +- doc/nimsuggest.md | 2 +- doc/readme.txt | 2 +- doc/sets_fragment.txt | 6 ++---- doc/tools.md | 2 +- doc/tut3.md | 2 +- lib/system.nim | 26 +++++++++++++------------- 12 files changed, 28 insertions(+), 34 deletions(-) diff --git a/compiler/readme.md b/compiler/readme.md index ac47508dc4e9..4a197991c42e 100644 --- a/compiler/readme.md +++ b/compiler/readme.md @@ -4,4 +4,4 @@ - Note that this code has been translated from a bootstrapping version written in Pascal. - So the code is **not** a poster child of good Nim code. -See [Internals of the Nim Compiler](https://nim-lang.org/docs/intern.html) for more information. +See [Internals of the Nim Compiler](https://nim-lang.github.io/Nim/intern.html) for more information. diff --git a/doc/advopt.txt b/doc/advopt.txt index 8362bbf4cba4..e4d11081ae48 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -48,7 +48,7 @@ Advanced options: --unitsep:on|off use the ASCII unit separator (31) between error messages, useful for IDE-like tooling --declaredLocs:on|off show declaration locations in messages - --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. + --spellSuggest:num show at most `num >= 0` spelling suggestions on typos. if `num` is not specified (or `auto`), return an implementation defined set of suggestions. --hints:on|off|list. `on|off` enables or disables hints. diff --git a/doc/filelist.txt b/doc/filelist.txt index 74995f154f37..5522414fe6b7 100644 --- a/doc/filelist.txt +++ b/doc/filelist.txt @@ -31,16 +31,14 @@ semstmts contains the semantic checking phase for statements semtypes contains the semantic checking phase for types seminst instantiation of generic procs and types semfold contains code to deal with constant folding -semthreads deep program analysis for threads -evals contains an AST interpreter for compile time evaluation +sempass2 Second semantic checking pass over the AST +vm contains an AST interpreter for compile time evaluation pragmas semantic checking of pragmas idents implements a general mapping from identifiers to an internal representation (`PIdent`) that is used so that a simple id-comparison suffices to establish whether two Nim identifiers are equivalent -ropes implements long strings represented as trees for - lazy evaluation; used mainly by the code generators transf transformations on the AST that need to be done before code generation diff --git a/doc/manual.md b/doc/manual.md index aa53c4512f90..1c3e668791ef 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -2978,9 +2978,9 @@ ref or pointer type nil procedural type nil sequence `@[]` string `""` -`tuple[x: A, y: B, ...]` (default(A), default(B), ...) +`tuple[x: A, y: B, ...]` (zeroDefault(A), zeroDefault(B), ...) (analogous for objects) -`array[0..., T]` `[default(T), ...]` +`array[0..., T]` `[zeroDefault(T), ...]` `range[T]` default(T); this may be out of the valid range T = enum `cast[T](0)`; this may be an invalid value ============================ ============================================== @@ -8735,9 +8735,7 @@ model low level lockfree mechanisms: The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]` -in order to support *multi lock* statements. Why these are essential is -explained in the [lock levels](manual_experimental.md#lock-levels) section -of the experimental manual. +in order to support *multi lock* statements. ### Protecting general locations diff --git a/doc/mm.md b/doc/mm.md index 9d1fc1955988..431517b176ba 100644 --- a/doc/mm.md +++ b/doc/mm.md @@ -38,7 +38,7 @@ instead entire subgraphs are *moved* between threads. The Nim compiler also aggr optimizes away RC ops and exploits [move semantics](destructors.html#move-semantics). Nim performs a fair share of optimizations for ARC/ORC; you can inspect what it did -to your time critical function via `--expandArc:functionName`. +to your time critical function via `--expandArc:functionName`. Likewise, you can inspect the whole module via `--expandArc:fileName`. `--mm:arc` uses the same mechanism as `--mm:orc`, but it leaves out the cycle collector. Both ARC and ORC offer deterministic performance for `hard realtime`:idx: systems, but diff --git a/doc/nims.md b/doc/nims.md index 2409bb577d56..01cdf47c3541 100644 --- a/doc/nims.md +++ b/doc/nims.md @@ -329,7 +329,7 @@ Evolving Scripting language NimScript evolves together with Nim, [occasionally new features might become available on NimScript]( -https://github.com/nim-lang/Nim/pulls?utf8=%E2%9C%93&q=nimscript), +https://github.com/nim-lang/Nim/pulls?q=nimscript+is%3Amerged), adapted from compiled Nim or added as new features on both. Scripting Language with a Package Manager diff --git a/doc/nimsuggest.md b/doc/nimsuggest.md index 97cb2b1fabbc..3d076a6f5077 100644 --- a/doc/nimsuggest.md +++ b/doc/nimsuggest.md @@ -49,7 +49,7 @@ via sockets is more reasonable so that is the default. It listens to port 6000 by default. Nimsuggest is basically a frontend for the nim compiler so `--path`:option: flags and -[config files](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files) +[config files](nimc.html#compiler-usage-configuration-files) can be used to specify additional dependencies like `nimsuggest --stdin --debug --path:"dependencies" myproject.nim`:cmd:. diff --git a/doc/readme.txt b/doc/readme.txt index 7b1a445b50c1..1157752b93c3 100644 --- a/doc/readme.txt +++ b/doc/readme.txt @@ -3,5 +3,5 @@ Nim's documentation system ============================ This folder contains Nim's documentation. The documentation -is written in a format called *reStructuredText*, a markup language that reads +is written in a format called *Markdown*, a markup language that reads like ASCII and can be converted to HTML automatically! diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt index 6e0991a9eb93..35b1fc023860 100644 --- a/doc/sets_fragment.txt +++ b/doc/sets_fragment.txt @@ -17,15 +17,13 @@ The reason is that sets are implemented as high performance bit vectors. Attempting to declare a set with a larger type will result in an error: ```nim - var s: set[int64] # Error: set is too large; use `std/sets` for ordinal types # with more than 2^16 elements - ``` **Note:** Nim also offers [hash sets](sets.html) (which you need to import -with `import sets`), which have no such restrictions. +with `import std/sets`), which have no such restrictions. Sets can be constructed via the set constructor: `{}` is the empty set. The empty set is type compatible with any concrete set type. The constructor @@ -41,7 +39,7 @@ can also be used to include elements (and ranges of elements): # from '0' to '9' ``` -The module [`std/setutils`](https://nim-lang.org/docs/setutils.html) provides a way to initialize a set from an iterable: +The module [`std/setutils`](setutils.html) provides a way to initialize a set from an iterable: ```nim import std/setutils diff --git a/doc/tools.md b/doc/tools.md index 3eda60b13c09..baf7ce38641d 100644 --- a/doc/tools.md +++ b/doc/tools.md @@ -37,7 +37,7 @@ The standard distribution ships with the following tools: | `nimpretty`:cmd: is a Nim source code beautifier, to format code according to the official style guide. -- | [testament](https://nim-lang.github.io/Nim/testament.html) +- | [testament](testament.html) | `testament`:cmd: is an advanced automatic *unittests runner* for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, JS, etc), diff --git a/doc/tut3.md b/doc/tut3.md index ccb2eb809136..67f49c87935d 100644 --- a/doc/tut3.md +++ b/doc/tut3.md @@ -366,7 +366,7 @@ recommended way. But still `strformat` is a good example for a practical use case for a macro that is slightly more complex than the `assert` macro. -[Strformat](https://github.com/nim-lang/Nim/blob/5845716df8c96157a047c2bd6bcdd795a7a2b9b1/lib/pure/strformat.nim#L280) +[Strformat](https://github.com/nim-lang/Nim/blob/devel/lib/pure/strformat.nim) Ast Pattern Matching -------------------- diff --git a/lib/system.nim b/lib/system.nim index 845ab58e66de..94e897b2bebd 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -25,7 +25,11 @@ include "system/basic_types" func zeroDefault*[T](_: typedesc[T]): T {.magic: "ZeroDefault".} = - ## returns the default value of the type `T`. + ## Returns the binary zeros representation of the type `T`. It ignores + ## default fields of an object. + ## + ## See also: + ## * `default <#default,typedesc[T]>`_ include "system/compilation" @@ -916,22 +920,18 @@ proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEff ## ``` proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} = - ## returns the default value of the type `T`. + ## Returns the default value of the type `T`. Contrary to `zeroDefault`, it takes default fields + ## of an object into consideration. + ## + ## See also: + ## * `zeroDefault <#zeroDefault,typedesc[T]>`_ + ## runnableExamples: assert (int, float).default == (0, 0.0) - # note: `var a = default(T)` is usually the same as `var a: T` and (currently) generates - # a value whose binary representation is all 0, regardless of whether this - # would violate type constraints such as `range`, `not nil`, etc. This - # property is required to implement certain algorithms efficiently which - # may require intermediate invalid states. type Foo = object a: range[2..6] - var a1: range[2..6] # currently, this compiles - # var a2: Foo # currently, this errors: Error: The Foo type doesn't have a default value. - # var a3 = Foo() # ditto - var a3 = Foo.default # this works, but generates a `UnsafeDefault` warning. - # note: the doc comment also explains why `default` can't be implemented - # via: `template default*[T](t: typedesc[T]): T = (var v: T; v)` + var x = Foo.default + assert x.a == 2 proc reset*[T](obj: var T) {.noSideEffect.} = From d9a24b9b81e6a4e5f723951ec89990e35a9f7d44 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:08:40 +0800 Subject: [PATCH 037/347] =?UTF-8?q?fixes=20#22208;=20Ambiguous=20error=20w?= =?UTF-8?q?hen=20import=20modules=20with=20same=20names=20but=E2=80=A6=20(?= =?UTF-8?q?#22211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #22208; Ambiguous error when import modules with same names but different aliases --- compiler/importer.nim | 2 +- parse/pragmas.nim | 3 +++ tests/import/buzz/m21496.nim | 2 +- tests/import/fizz/m21496.nim | 2 +- tests/import/t21496.nim | 2 +- tests/import/t22208.nim | 6 ++++++ 6 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 parse/pragmas.nim create mode 100644 tests/import/t22208.nim diff --git a/compiler/importer.nim b/compiler/importer.nim index 39a447b70937..54489ada4e9d 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -250,7 +250,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool) result.options.incl optImportHidden c.unusedImports.add((result, n.info)) c.importModuleMap[result.id] = realModule.id - c.importModuleLookup.mgetOrPut(realModule.name.id, @[]).addUnique realModule.id + c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = var ret: typeof(result) diff --git a/parse/pragmas.nim b/parse/pragmas.nim new file mode 100644 index 000000000000..bf77a28420e6 --- /dev/null +++ b/parse/pragmas.nim @@ -0,0 +1,3 @@ +# parse/pragmas.nim content + +proc foo*() = discard \ No newline at end of file diff --git a/tests/import/buzz/m21496.nim b/tests/import/buzz/m21496.nim index 7c87e2c04af5..677324000be4 100644 --- a/tests/import/buzz/m21496.nim +++ b/tests/import/buzz/m21496.nim @@ -1 +1 @@ -proc fb* = echo "buzz!" \ No newline at end of file +proc fb*: string = "buzz!" \ No newline at end of file diff --git a/tests/import/fizz/m21496.nim b/tests/import/fizz/m21496.nim index 834c11eae060..525b653d666e 100644 --- a/tests/import/fizz/m21496.nim +++ b/tests/import/fizz/m21496.nim @@ -1 +1 @@ -proc fb* = echo "fizz!" \ No newline at end of file +proc fb*: string = "fizz!" \ No newline at end of file diff --git a/tests/import/t21496.nim b/tests/import/t21496.nim index b49d830b0ed8..1a4907b1236a 100644 --- a/tests/import/t21496.nim +++ b/tests/import/t21496.nim @@ -6,4 +6,4 @@ import fizz/m21496, buzz/m21496 # bug #21496 -m21496.fb() +discard m21496.fb() diff --git a/tests/import/t22208.nim b/tests/import/t22208.nim new file mode 100644 index 000000000000..b935d751cb96 --- /dev/null +++ b/tests/import/t22208.nim @@ -0,0 +1,6 @@ +import fizz/m21496 as alas +import buzz/m21496 + +# bug #21496 + +doAssert m21496.fb() == "buzz!" From c513e37a7052d44b789b6ec8ccfc94885193a7b5 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:15:44 +0800 Subject: [PATCH 038/347] =?UTF-8?q?fixes=20#22212;=20Compile=20error=20whe?= =?UTF-8?q?n=20running=20a=20Nimscript=20that=20compares=20se=E2=80=A6=20(?= =?UTF-8?q?#22213)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #22212; Compile error when running a Nimscript that compares seq with switch("mm", "arc") --- compiler/commands.nim | 3 ++- compiler/scriptconfig.nim | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 333a0f0d031f..e12b047a6521 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -527,10 +527,11 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) = if conf.exc == excNone and conf.backend != backendCpp: conf.exc = excGoto -proc unregisterArcOrc(conf: ConfigRef) = +proc unregisterArcOrc*(conf: ConfigRef) = undefSymbol(conf.symbols, "gcdestructors") undefSymbol(conf.symbols, "gcarc") undefSymbol(conf.symbols, "gcorc") + undefSymbol(conf.symbols, "gcatomicarc") undefSymbol(conf.symbols, "nimSeqsV2") undefSymbol(conf.symbols, "nimV2") excl conf.globalOptions, optSeqDestructors diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 21b0eb19561c..46751fe4efe8 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -17,7 +17,7 @@ import pathutils, pipelines when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] # we support 'cmpIgnoreStyle' natively for efficiency: from strutils import cmpIgnoreStyle, contains @@ -207,8 +207,8 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; let oldGlobalOptions = conf.globalOptions let oldSelectedGC = conf.selectedGC - undefSymbol(conf.symbols, "nimv2") - conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors} + unregisterArcOrc(conf) + conf.globalOptions.excl optOwnedRefs conf.selectedGC = gcUnselected var m = graph.makeModule(scriptName) @@ -230,6 +230,17 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}: conf.globalOptions.incl {optTinyRtti, optSeqDestructors} defineSymbol(conf.symbols, "nimv2") + defineSymbol(conf.symbols, "gcdestructors") + defineSymbol(conf.symbols, "nimSeqsV2") + case conf.selectedGC + of gcArc: + defineSymbol(conf.symbols, "gcarc") + of gcOrc: + defineSymbol(conf.symbols, "gcorc") + of gcAtomicArc: + defineSymbol(conf.symbols, "gcatomicarc") + else: + doAssert false, "unreachable" # ensure we load 'system.nim' again for the real non-config stuff! resetSystemArtifacts(graph) From 31ba1046fc91f110117b9e1cbd28d1afb4432060 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 4 Jul 2023 10:58:14 +0800 Subject: [PATCH 039/347] add a test case for #22190 in case of regression (#22217) --- tests/vm/tvmmisc.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 5ddb544110c9..1a8f68ee8f2e 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -712,3 +712,24 @@ block: var y = "356" merge(x, y) doAssert x == "3bc" + +block: # bug #22190 + type + EVMFork = enum + Berlin + Istanbul + Shanghai + + const + Vm2OpAllForks = + {EVMFork.low .. EVMFork.high} + + vm2OpExecBlockData = [(forks: Vm2OpAllForks)] + + proc mkOpTable(selected: EVMFork): bool = + selected notin vm2OpExecBlockData[0].forks + + const + tab = mkOpTable(Berlin) + + doAssert not tab From 86ff37fab8656f7acd08fc9c3fa237f9e022dbf4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 4 Jul 2023 23:35:25 +0200 Subject: [PATCH 040/347] fixes #22138 (#22221) --- compiler/sigmatch.nim | 9 +++++++-- tests/lent/tlent_from_var.nim | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index b2f52ba120ef..e90f1524b6b9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1140,7 +1140,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric - + of tyFromExpr: if c.c.inGenericContext > 0: # generic type bodies can sometimes compile call expressions @@ -1950,7 +1950,12 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") result.add c.graph.emptyNode - result.add arg + if arg.typ != nil and arg.typ.kind == tyLent: + let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ[0]) + a.add arg + result.add a + else: + result.add arg proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} = let aa = isAssignable(nil, n) diff --git a/tests/lent/tlent_from_var.nim b/tests/lent/tlent_from_var.nim index 912390dc1648..d61ff6dc009a 100644 --- a/tests/lent/tlent_from_var.nim +++ b/tests/lent/tlent_from_var.nim @@ -30,3 +30,23 @@ let x2 = x.byLentVar let xs2 = xs.byLentVar echo xs2 + +# bug #22138 + +type Xxx = object + +type + Opt[T] = object + case oResultPrivate*: bool + of false: + discard + of true: + vResultPrivate*: T + +func value*[T: not void](self: Opt[T]): lent T {.inline.} = + self.vResultPrivate +template get*[T: not void](self: Opt[T]): T = self.value() + +method connect*( + self: Opt[(int, int)]) = + discard self.get()[0] From 145e002c74c4393b9ac71d5a8cfe066fbf38a048 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 5 Jul 2023 17:21:57 +0800 Subject: [PATCH 041/347] fixes #22132; hoisted openArray params result in erroneous code (#22224) --- compiler/injectdestructors.nim | 5 ++++- tests/arc/topenarray.nim | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index c5920f4b34cb..d7f4e38d2c71 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1072,7 +1072,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy if sameLocation(dest, ri): # rule (self-assignment-removal): result = newNodeI(nkEmpty, dest.info) - elif isCursor(dest): + elif isCursor(dest) or dest.typ.kind in {tyOpenArray, tyVarargs}: + # hoisted openArray parameters might end up here + # openArray types don't have a lifted assignment operation (it's empty) + # bug #22132 case ri.kind: of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags) diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim index 0e45f3ec7ba0..67c512e4f8c7 100644 --- a/tests/arc/topenarray.nim +++ b/tests/arc/topenarray.nim @@ -50,3 +50,21 @@ proc f(a: var string) = var a = "Hello" f(a) doAssert a == "Hallo" + +# bug #22132 +block: + func foo[T](arr: openArray[T], idx: int = arr.low): string = + doAssert idx == 0 + return $arr + + let bug = ["0", "c", "a"] + + let str = foo(bug) + + const expected = """["0", "c", "a"]""" + doAssert str == expected + + const noBugConst = ["0", "c", "a"] + doAssert foo(noBugConst) == expected + let noBugSeq = @["0", "c", "a"] + doAssert foo(noBugSeq) == expected From 7616e6ee2b48a3c3504684f3324a42e95bd73790 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Thu, 6 Jul 2023 16:18:47 +1000 Subject: [PATCH 042/347] Fix concepts with doc comments (#22228) * Add testcase This tries to use a concept with a doc comment which currently leads to a segfault * Ignore nil nodes which happen when there are doc comments in new style concept This was done instead of semming the comments since `semConceptDecl` says it only supports lists of actual statements * Go with alternative fix: Sem comments but ignore them Since `nil` could mean anything it is best to not silently ignore it (In case another nil problem happens in future Also fix test case so it isn't an infinite loop --- compiler/concepts.nim | 4 +++- tests/concepts/tconcepts.nim | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 6a383a937ca5..c980cf7ef27c 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -62,7 +62,7 @@ proc semConceptDecl(c: PContext; n: PNode): PNode = result[i] = n[i] result[^1] = semConceptDecl(c, n[^1]) of nkCommentStmt: - discard + result = n else: localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n)) result = n @@ -306,6 +306,8 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool = result = matchSyms(c, n, {skMethod}, m) of nkIteratorDef: result = matchSyms(c, n, {skIterator}, m) + of nkCommentStmt: + result = true else: # error was reported earlier. result = false diff --git a/tests/concepts/tconcepts.nim b/tests/concepts/tconcepts.nim index acdff6f24612..ea3ddc4017e5 100644 --- a/tests/concepts/tconcepts.nim +++ b/tests/concepts/tconcepts.nim @@ -31,6 +31,7 @@ e 20 10 5 +9 ''' """ @@ -438,3 +439,13 @@ import mvarconcept block tvar: # bug #2346, bug #2404 echo randomInt(5) + +block tcomment: + type + Foo = concept + ## Some comment + proc bar(x: Self) + + proc bar(x: int) = echo x + proc foo(x: Foo) = x.bar + foo(9) From dfa0d2569e772d6fc4568796db03f65dc2e94771 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 6 Jul 2023 17:14:42 +0800 Subject: [PATCH 043/347] alternative to #22219; adds a pointer wrapper for T destructor (#22225) * alternative to #22219; adds a pointer wrapper for T destructor * clean up and add comments * Update compiler/ccgtypes.nim * tidy up * fixes comments * fixes cpp * fixes cpp --- compiler/ccgexprs.nim | 4 ---- compiler/ccgtypes.nim | 55 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index e2498b57b4c7..c585290cd62b 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1919,10 +1919,6 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") -proc makePtrType(baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, nextTypeId idgen, baseType.owner) - addSonSkipIntLit(result, baseType, idgen) - proc makeAddr(n: PNode; idgen: IdGenerator): PNode = if n.kind == nkHiddenAddr: result = n diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f808fa93ea3b..9f4b7aa7cb72 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1517,6 +1517,51 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 +proc makePtrType(baseType: PType; idgen: IdGenerator): PType = + result = newType(tyPtr, nextTypeId idgen, baseType.owner) + addSonSkipIntLit(result, baseType, idgen) + +proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; + info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym = + # the wrapper is roughly like: + # proc rttiDestroy(x: pointer) = + # `=destroy`(cast[ptr T](x)[]) + let procname = getIdent(g.cache, "rttiDestroy") + result = newSym(skProc, procname, idgen, owner, info) + let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info) + + dest.typ = getSysType(g, info, tyPointer) + + result.typ = newProcType(info, nextTypeId(idgen), owner) + result.typ.addParam dest + + var n = newNodeI(nkProcDef, info, bodyPos+1) + for i in 0.. Date: Thu, 6 Jul 2023 15:15:50 +0200 Subject: [PATCH 044/347] fixes #22175 (#22229) --- compiler/ccgexprs.nim | 7 +++++-- tests/arc/t17812.nim | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index c585290cd62b..00781a31d80c 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2357,8 +2357,11 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)]) else: if d.k == locNone: getTemp(p, n.typ, d) - genAssignment(p, d, a, {}) - if p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc}: + if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + genAssignment(p, d, a, {}) + else: + let flags = if not canMove(p, n[1], d): {needToCopy} else: {} + genAssignment(p, d, a, flags) resetLoc(p, a) proc genDestroy(p: BProc; n: PNode) = diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim index bcd5f3a932f4..dd8ac89b07fa 100644 --- a/tests/arc/t17812.nim +++ b/tests/arc/t17812.nim @@ -27,3 +27,15 @@ block: # bug #17812 proc `$`(o: MyObj): string = o.repr doAssert ($MyObj()).len > 0 + +# bug #22175 + +type Xxx = object + value: string + +proc complete(xxx: ref Xxx, v: sink string) = + xxx.value = move(v) + +let yyy = (ref Xxx)() + +yyy.complete("test") From 148ff74c9385909fc1e10c8d29499fb815a94fb8 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 7 Jul 2023 04:18:40 -0300 Subject: [PATCH 045/347] Fix #21401 (#22232) --- lib/system/seqs_v2.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 4bebf4a82731..9aa4e71b986f 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -11,6 +11,11 @@ # import typetraits # strs already imported allocateds for us. + +# Some optimizations here may be not to empty-seq-initialize some symbols, then StrictNotNil complains. +{.push warning[StrictNotNil]: off.} # See https://github.com/nim-lang/Nim/issues/21401 + + proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} ## Default seq implementation used by Nim's core. @@ -149,3 +154,6 @@ func capacity*[T](self: seq[T]): int {.inline.} = {.cast(noSideEffect).}: let sek = unsafeAddr self result = capacityImpl(cast[ptr NimSeqV2](sek)[]) + + +{.pop.} # See https://github.com/nim-lang/Nim/issues/21401 From 2e987cb75a02be74b53150774316036f0befba88 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 7 Jul 2023 04:38:37 -0600 Subject: [PATCH 046/347] Tolerate markup errors for doc comments (#19607) (#22235) Follow-up to #21576 (for solving #19607). 1) errors in Markdown mode for `.nim` doc comments are reported with red color but allow to generate `.html` with the comment represented by literate block (monospaced text). We suppose that it's what people want for (supposedly) small doc comments. And this behavior is also a bit more Markdown-ish in the sense that Markdown generally does not have the concept of parsing error. - However, for standalone `.md` it's **not** applied because for large files the consequences are way bigger. (In {.doctype: rst.} mode the behavior is the same as before -- report the error and stop.) In future, when our parser can handle Markdown without errors according to the spec, this code will most probably be not needed. --- compiler/docgen.nim | 16 +++++++++++++--- compiler/msgs.nim | 6 ++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 81e23b0693ac..958d804b34c2 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -259,7 +259,16 @@ template declareClosures(currentFilename: AbsoluteFile, destFile: string) = of mwUnusedImportdoc: k = warnRstUnusedImportdoc of mwRstStyle: k = warnRstStyle {.gcsafe.}: - globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg) + let errorsAsWarnings = (roPreferMarkdown in d.sharedState.options) and + not d.standaloneDoc # not tolerate errors in .rst/.md files + if whichMsgClass(msgKind) == mcError and errorsAsWarnings: + liMessage(conf, newLineInfo(conf, AbsoluteFile filename, line, col), + k, arg, doNothing, instLoc(), ignoreError=true) + # when our Markdown parser fails, we currently can only terminate the + # parsing (and then we will return monospaced text instead of markup): + raiseRecoverableError("") + else: + globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg) proc docgenFindFile(s: string): string {.gcsafe.} = result = options.findFile(conf, s).string @@ -311,8 +320,9 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, standaloneDoc = false, preferMarkdown = true, hasToc = true): PDoc = let destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string - declareClosures(currentFilename = filename, destFile = destFile) new(result) + let d = result # pass `d` to `declareClosures`: + declareClosures(currentFilename = filename, destFile = destFile) result.module = module result.conf = conf result.cache = cache @@ -424,7 +434,7 @@ proc genComment(d: PDoc, n: PNode): PRstNode = toColumn(n.info) + DocColOffset, d.conf, d.sharedState) except ERecoverableError: - result = nil + result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)]) proc genRecCommentAux(d: PDoc, n: PNode): PRstNode = if n == nil: return nil diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 05ace315e362..03d38398ab1f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -511,7 +511,8 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, - eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.gcsafe, noinline.} = + eh: TErrorHandling, info2: InstantiationInfo, isRaw = false, + ignoreError = false) {.gcsafe, noinline.} = var title: string color: ForegroundColor @@ -576,7 +577,8 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, resetStyle, conf.unitSep) - handleError(conf, msg, eh, s, ignoreMsg) + if not ignoreError: + handleError(conf, msg, eh, s, ignoreMsg) if msg in fatalMsgs: # most likely would have died here but just in case, we restore state conf.m.errorOutputs = errorOutputsOld From 54d7c9fdf8ab8690f8f2f97e31a1ec226175cca3 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 7 Jul 2023 07:39:59 -0300 Subject: [PATCH 047/347] Fix #21595 (#22233) * . * Fix #21595 --- config/nim.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index a9dba347a028..efb0581218d7 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -354,7 +354,7 @@ tcc.options.always = "-w" icl.cpp.options.always %= "${icl.cpp.options.always} /Qipo" gcc.options.always %= "${gcc.options.always} -flto=auto" gcc.cpp.options.always %= "${gcc.cpp.options.always} -flto=auto" - gcc.options.linker %= "${gcc.options.linker} -flto=auto" + gcc.options.linker %= "${gcc.options.linker} -flto=auto -Wno-stringop-overflow" # https://github.com/nim-lang/Nim/issues/21595 gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -flto=auto" @end @if strip: @@ -363,4 +363,3 @@ tcc.options.always = "-w" clang.options.linker %= "${clang.options.linker} -s" clang.cpp.options.linker %= "${clang.cpp.options.linker} -s" @end - From 6ec10a4c9182c513fecd17fde4a81a3a007bb2e9 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 7 Jul 2023 18:40:29 +0800 Subject: [PATCH 048/347] fixes #21730; adds pkgs2 as well when nimbleDir is set (#22234) --- compiler/commands.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/commands.nim b/compiler/commands.nim index e12b047a6521..5396cbe0d37d 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -614,6 +614,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; var path = processPath(conf, arg, info, notRelativeToProj=true) let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR") if not nimbleDir.isEmpty and pass == passPP: + path = nimbleDir / RelativeDir"pkgs2" + nimblePath(conf, path, info) path = nimbleDir / RelativeDir"pkgs" nimblePath(conf, path, info) of "nonimblepath", "nobabelpath": From 1b132ddaa2734fc43a9c172407fc968cfeec4a24 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Mon, 10 Jul 2023 16:34:10 +1000 Subject: [PATCH 049/347] Fix nimsuggest not showing suggestions for imported tuples (#22241) * Add tests Also test if exported all tuple fields works. This seems like a hacky solution so will try and dive further to find a better solution * Always suggest tuple fields if it passes the filter If the tuple we are accessing is in scope then all the fields will also be in scope * Update tests so line numbers are correct --- compiler/suggest.nim | 10 +++++++++- nimsuggest/tests/module_20265.nim | 6 ++++++ nimsuggest/tests/t20265_1.nim | 8 ++++++++ nimsuggest/tests/t20265_2.nim | 8 ++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 nimsuggest/tests/module_20265.nim create mode 100644 nimsuggest/tests/t20265_1.nim create mode 100644 nimsuggest/tests/t20265_2.nim diff --git a/compiler/suggest.nim b/compiler/suggest.nim index f41f35519aa0..ca19b2460563 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -440,7 +440,15 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) if t[0] == nil: break t = skipTypes(t[0], skipPtrs) elif typ.kind == tyTuple and typ.n != nil: - suggestSymList(c, typ.n, field, n.info, outputs) + # All tuple fields are in scope + # So go through each field and add it to the suggestions (If it passes the filter) + for node in typ.n: + if node.kind == nkSym: + let s = node.sym + var pm: PrefixMatch + if filterSym(s, field, pm): + outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, n.info, + s.getQuality, pm, c.inTypeContext > 0, 0)) suggestOperations(c, n, field, orig, outputs) if typ != orig: diff --git a/nimsuggest/tests/module_20265.nim b/nimsuggest/tests/module_20265.nim new file mode 100644 index 000000000000..24b7d10c9398 --- /dev/null +++ b/nimsuggest/tests/module_20265.nim @@ -0,0 +1,6 @@ +type A* = tuple + a: int + b: int + +var x*: A = (a: 2, b: 10) +var y* = (a: 2, b: 10) diff --git a/nimsuggest/tests/t20265_1.nim b/nimsuggest/tests/t20265_1.nim new file mode 100644 index 000000000000..553b3d5451bf --- /dev/null +++ b/nimsuggest/tests/t20265_1.nim @@ -0,0 +1,8 @@ +discard """ +$nimsuggest --tester $file +>sug $1 +sug;;skField;;a;;int;;*module_20265.nim;;6;;10;;"";;100;;None +sug;;skField;;b;;int;;*module_20265.nim;;6;;16;;"";;100;;None +""" +import module_20265 +y.#[!]# diff --git a/nimsuggest/tests/t20265_2.nim b/nimsuggest/tests/t20265_2.nim new file mode 100644 index 000000000000..33edf2d9ad93 --- /dev/null +++ b/nimsuggest/tests/t20265_2.nim @@ -0,0 +1,8 @@ +discard """ +$nimsuggest --tester $file +>sug $1 +sug;;skField;;a;;int;;*module_20265.nim;;2;;2;;"";;100;;None +sug;;skField;;b;;int;;*module_20265.nim;;3;;2;;"";;100;;None +""" +import module_20265 +x.#[!]# From ecc6ab7ee06a5969fd871585de8b93120bd58a2b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 10 Jul 2023 16:31:13 +0800 Subject: [PATCH 050/347] fixes #22237; fixes #21160; wrong cursor on unowned parameters in the for loop in ORC (#22240) fixes #22237; fixes #21160; wrong cursor on unowned parameters --- compiler/transf.nim | 17 ++++++++++++-- tests/arc/t22237.nim | 55 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tests/arc/t22237.nim diff --git a/compiler/transf.nim b/compiler/transf.nim index 6ff1da899ae7..d0428b725d4a 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -654,7 +654,7 @@ proc findWrongOwners(c: PTransf, n: PNode) = else: for i in 0.. Date: Mon, 10 Jul 2023 15:27:28 +0300 Subject: [PATCH 051/347] Rename `seq.add` parameter to be consistent with `refc` (#22244) --- lib/system/seqs_v2.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 9aa4e71b986f..efc2919fd487 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -102,7 +102,7 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = for i in oldLen .. newLen-1: xu.p.data[i] = value -proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} = +proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} = ## Generic proc for adding a data item `y` to a container `x`. ## ## For containers that have an order, `add` means *append*. New generic @@ -119,7 +119,7 @@ proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffec # copyMem(). This is fine as know by construction that # in `xu.p.data[oldLen]` there is nothing to destroy. # We also save the `wasMoved + destroy` pair for the sink parameter. - xu.p.data[oldLen] = value + xu.p.data[oldLen] = y proc setLen[T](s: var seq[T], newlen: Natural) = {.noSideEffect.}: From 9471b5f964395f9b4f4f673239d4375e1910bfaa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 12 Jul 2023 01:01:18 +0800 Subject: [PATCH 052/347] fixes #22256; fixes GC_disableOrc overflow (#22257) --- lib/system/orc.nim | 2 +- tests/gc/tdisable_orc.nim | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/gc/tdisable_orc.nim diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 1935407102ee..b7b98c3404a6 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -411,7 +411,7 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) = if roots.d == nil: init(roots) add(roots, s, desc) - if roots.len >= rootsThreshold+defaultThreshold: + if roots.len - defaultThreshold >= rootsThreshold: collectCycles() when logOrc: writeCell("[added root]", s, desc) diff --git a/tests/gc/tdisable_orc.nim b/tests/gc/tdisable_orc.nim new file mode 100644 index 000000000000..b5f161c79197 --- /dev/null +++ b/tests/gc/tdisable_orc.nim @@ -0,0 +1,9 @@ +discard """ + joinable: false +""" + +import std/asyncdispatch + +# bug #22256 +GC_disableMarkAndSweep() +waitFor sleepAsync(1000) From 3b377937dec9dd6e7a68e9c95c4ec47d6d21d689 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 14 Jul 2023 18:40:24 +0800 Subject: [PATCH 053/347] gendepends now supports pkgs2 (#22277) --- compiler/depends.nim | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/depends.nim b/compiler/depends.nim index 823e0f970a4c..fa5891ba3c2a 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -63,12 +63,16 @@ proc toNimblePath(s: string, isStdlib: bool): string = sub.add "/pkgs/" var start = s.find(sub) if start < 0: - result = s - else: - start += sub.len - start += skipUntil(s, '/', start) - start += 1 - result = pkgPrefix & s[start..^1] + sub[^1] = '2' + sub.add '/' + start = s.find(sub) # /pkgs2 + if start < 0: + return s + + start += sub.len + start += skipUntil(s, '/', start) + start += 1 + result = pkgPrefix & s[start..^1] proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) = doAssert n.kind == nkSym, $n.kind From db1ce4ff1246f9b60fe6c6498c72b3e9d6abb8c8 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 14 Jul 2023 07:40:57 -0300 Subject: [PATCH 054/347] Fix #22273 (#22275) * Fix #22273 --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 94e897b2bebd..50debcc89504 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1621,7 +1621,7 @@ when not defined(js) and defined(nimV2): align: int16 depth: int16 display: ptr UncheckedArray[uint32] # classToken - when defined(nimTypeNames): + when defined(nimTypeNames) or defined(nimArcIds): name: cstring traceImpl: pointer typeInfoV1: pointer # for backwards compat, usually nil From f9280090f623d8fddbf753ec50e0ecd21f388d00 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 14 Jul 2023 20:44:02 +0800 Subject: [PATCH 055/347] fixes idx properly (#22280) --- lib/packages/docutils/rstgen.nim | 21 ++++++++++++--------- lib/packages/docutils/rstidx.nim | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index ec9926863cde..008dff60aa80 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -591,16 +591,11 @@ proc readIndexDir*(dir: string): if path.endsWith(IndexExt): var (fileEntries, title) = parseIdxFile(path) # Depending on type add this to the list of symbols or table of APIs. - if title.kind in {ieNimTitle, ieIdxRole}: + + if title.kind == ieNimTitle: for i in 0 ..< fileEntries.len: - if title.kind == ieIdxRole: - # Don't add to symbols TOC entries (they start with a whitespace). - let toc = fileEntries[i].linkTitle - if toc.len > 0 and toc[0] == ' ': - continue - else: - if fileEntries[i].kind != ieNim: - continue + if fileEntries[i].kind != ieNim: + continue # Ok, non TOC entry, add it. setLen(result.symbols, L + 1) result.symbols[L] = fileEntries[i] @@ -618,6 +613,14 @@ proc readIndexDir*(dir: string): title.aux = "doc_toc_" & $result.docs.len result.docs[title] = fileEntries + for i in 0 ..< fileEntries.len: + if fileEntries[i].kind != ieIdxRole: + continue + + setLen(result.symbols, L + 1) + result.symbols[L] = fileEntries[i] + inc L + proc mergeIndexes*(dir: string): string = ## Merges all index files in `dir` and returns the generated index as HTML. ## diff --git a/lib/packages/docutils/rstidx.nim b/lib/packages/docutils/rstidx.nim index c48f44300cfe..236b8361ac53 100644 --- a/lib/packages/docutils/rstidx.nim +++ b/lib/packages/docutils/rstidx.nim @@ -121,7 +121,7 @@ proc parseIdxFile*(path: string): result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn result.fileEntries[f].line = parseInt(cols[5]) - if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle, ieIdxRole}: + if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}: result.title = result.fileEntries[f] inc f From 50d435cd3941f6c7f71a2e2b91cfa40a5a4efcb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Sun, 16 Jul 2023 16:01:34 +0100 Subject: [PATCH 056/347] Fixes Naive virtual crash the compiler fixes #22269 (#22271) * Fixes Naive virtual crash the compiler fixes #22269 * adds type specific test --- compiler/ccgtypes.nim | 2 +- tests/cpp/tvirtual.nim | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 9f4b7aa7cb72..307d1f2e0ef8 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -774,7 +774,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope, result = structOrUnion & " " & name result.add(getRecordDescAux(m, typ, name, baseType, check, hasField)) let desc = getRecordFields(m, typ, check) - if not hasField: + if not hasField and typ.itemId notin m.g.graph.memberProcsPerType: if desc == "": result.add("\tchar dummy;\n") elif typ.len == 1 and typ.n[0].kind == nkSym: diff --git a/tests/cpp/tvirtual.nim b/tests/cpp/tvirtual.nim index 0488ce07de5c..7acec21baafc 100644 --- a/tests/cpp/tvirtual.nim +++ b/tests/cpp/tvirtual.nim @@ -74,4 +74,8 @@ var val : int32 = 10 NimPrinter().printConstRef(message, val) NimPrinter().printConstRef2(message, val) +#bug 22269 +type Doo = object +proc naiveMember(x: Doo): int {. virtual .} = 2 +discard naiveMember(Doo()) From 17915d93bfb50aaff6c4bf77fe25707705e557c8 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Mon, 17 Jul 2023 03:46:18 +1000 Subject: [PATCH 057/347] Fix non-toplevel fields in objects not getting rendered (#22266) * Add example object into testproject The proc is there to check the case of an identDef being inside an identDef (We do want to render those even if they are not exported) * Add `inside` set to `TSrcGen` which allows us to see what context we are in This is done instead of adding another inXyz bool parameter We then use this to know if we are inside an object when rendering an nkIdentDefs (To know if we need to skip rendering it) * Update test files --- compiler/renderer.nim | 95 +++++++++++++++----- nimdoc/testproject/expected/testproject.html | 23 ++++- nimdoc/testproject/expected/testproject.idx | 1 + nimdoc/testproject/expected/theindex.html | 4 + nimdoc/testproject/testproject.nim | 8 ++ 5 files changed, 106 insertions(+), 25 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3f237c932255..ac4ff2a77e18 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -31,6 +31,10 @@ type length*: int16 sym*: PSym + Section = enum + GenericParams + ObjectDef + TRenderTokSeq* = seq[TRenderTok] TSrcGen* = object indent*: int @@ -45,7 +49,7 @@ type pendingWhitespace: int comStack*: seq[PNode] # comment stack flags*: TRenderFlags - inGenericParams: bool + inside: set[Section] # Keeps track of contexts we are in checkAnon: bool # we're in a context that can contain sfAnon inPragma: int when defined(nimpretty): @@ -73,6 +77,16 @@ proc isKeyword*(i: PIdent): bool = (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): result = true +proc isExported(n: PNode): bool = + ## Checks if an ident is exported. + ## This is meant to be used with idents in nkIdentDefs. + case n.kind + of nkPostfix: + n[0].ident.s == "*" and n[1].kind == nkIdent + of nkPragmaExpr: + n[0].isExported() + else: false + proc renderDefinitionName*(s: PSym, noQuotes = false): string = ## Returns the definition name of the symbol. ## @@ -85,6 +99,25 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string = else: result = '`' & x & '`' +template inside(g: var TSrcGen, section: Section, body: untyped) = + ## Runs `body` with `section` included in `g.inside`. + ## Removes it at the end of the body if `g` wasn't inside it + ## before the template. + let wasntInSection = section notin g.inside + g.inside.incl section + body + if wasntInSection: + g.inside.excl section + +template outside(g: var TSrcGen, section: Section, body: untyped) = + ## Temporarily removes `section` from `g.inside`. Adds it back + ## at the end of the body if `g` was inside it before the template + let wasInSection = section in g.inside + g.inside.excl section + body + if wasInSection: + g.inside.incl section + const IndentWidth = 2 longIndentWid = IndentWidth * 2 @@ -121,7 +154,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = g.flags = renderFlags g.pendingNL = -1 g.pendingWhitespace = -1 - g.inGenericParams = false + g.inside = {} g.config = config proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = @@ -831,14 +864,12 @@ proc gproc(g: var TSrcGen, n: PNode) = if n[patternPos].kind != nkEmpty: gpattern(g, n[patternPos]) - let oldInGenericParams = g.inGenericParams - g.inGenericParams = true - if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and - n[miscPos][1].kind != nkEmpty: - gsub(g, n[miscPos][1]) - else: - gsub(g, n[genericParamsPos]) - g.inGenericParams = oldInGenericParams + g.inside(GenericParams): + if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and + n[miscPos][1].kind != nkEmpty: + gsub(g, n[miscPos][1]) + else: + gsub(g, n[genericParamsPos]) gsub(g, n[paramsPos]) if renderNoPragmas notin g.flags: gsub(g, n[pragmasPos]) @@ -916,7 +947,7 @@ proc gasm(g: var TSrcGen, n: PNode) = gsub(g, n[1]) proc gident(g: var TSrcGen, n: PNode) = - if g.inGenericParams and n.kind == nkSym: + if GenericParams in g.inside and n.kind == nkSym: if sfAnon in n.sym.flags or (n.typ != nil and tfImplicitTypeParam in n.typ.flags): return @@ -1304,14 +1335,31 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n, pragmasPos) put(g, tkColon, ":") gsub(g, n, bodyPos) - of nkConstDef, nkIdentDefs: + of nkIdentDefs: + # Skip if this is a property in a type and its not exported + # (While also not allowing rendering of non exported fields) + if ObjectDef in g.inside and (not n[0].isExported() and renderNonExportedFields notin g.flags): + return + # We render the identDef without being inside the section incase we render something like + # y: proc (x: string) # (We wouldn't want to check if x is exported) + g.outside(ObjectDef): + gcomma(g, n, 0, -3) + if n.len >= 2 and n[^2].kind != nkEmpty: + putWithSpace(g, tkColon, ":") + gsub(g, n[^2], c) + elif n.referencesUsing and renderExpandUsing in g.flags: + putWithSpace(g, tkColon, ":") + gsub(g, newSymNode(n.origUsingType), c) + + if n.len >= 1 and n[^1].kind != nkEmpty: + put(g, tkSpaces, Space) + putWithSpace(g, tkEquals, "=") + gsub(g, n[^1], c) + of nkConstDef: gcomma(g, n, 0, -3) if n.len >= 2 and n[^2].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n[^2], c) - elif n.referencesUsing and renderExpandUsing in g.flags: - putWithSpace(g, tkColon, ":") - gsub(g, newSymNode(n.origUsingType), c) if n.len >= 1 and n[^1].kind != nkEmpty: put(g, tkSpaces, Space) @@ -1468,20 +1516,19 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkObjectTy: if n.len > 0: putWithSpace(g, tkObject, "object") - gsub(g, n[0]) - gsub(g, n[1]) - gcoms(g) - gsub(g, n[2]) + g.inside(ObjectDef): + gsub(g, n[0]) + gsub(g, n[1]) + gcoms(g) + gsub(g, n[2]) else: put(g, tkObject, "object") of nkRecList: indentNL(g) for i in 0..testproject
  • A
  • +
  • AnotherObject
  • B
  • Types The enum A. + + +
    +
    AnotherObject = object
    +  case x*: bool
    +  of true:
    +      y*: proc (x: string)
    +
    +  of false:
    +    
    +  
    +
    + + +
    @@ -424,7 +445,7 @@

    Types

    T19396 = object
       a*: int
    -
    +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index c29223a83339..ac9bbb2cede4 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -66,5 +66,6 @@ nim anything testproject.html#anything proc anything() 387 nim T19396 testproject.html#T19396 object T19396 392 nim somePragma testproject.html#somePragma.t template somePragma() 396 nim MyObject testproject.html#MyObject object MyObject 400 +nim AnotherObject testproject.html#AnotherObject object AnotherObject 405 nimgrp bar testproject.html#bar-procs-all proc 31 nimgrp baz testproject.html#baz-procs-all proc 34 diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 24e3cff1c96c..70916f7e0c62 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -52,6 +52,10 @@

    Index

  • utils: template aEnum(): untyped
+
AnotherObject:
anything:
  • testproject: proc anything()
  • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index d08a12544f28..d2d3fef3fd2f 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -400,3 +400,11 @@ type # bug #21483 MyObject* = object someString*: string ## This is a string annotated* {.somePragma.}: string ## This is an annotated string + +type + AnotherObject* = object + case x*: bool + of true: + y*: proc (x: string) + of false: + hidden: string From f16b94a9d7011bd00ebb3966d76ef8b2c0dfc752 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 18 Jul 2023 22:05:05 +0800 Subject: [PATCH 058/347] extend the skipAddr for potential types for destructors (#22265) extend the skipAddr for potential types --- compiler/semmagic.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index e7057053b727..f94d8dc33ebb 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -610,8 +610,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if op != nil: result[0] = newSymNode(op) - if op.typ != nil and op.typ.len == 2 and op.typ[1].kind != tyVar and - skipAddr(n[1]).typ.kind == tyDistinct: + if op.typ != nil and op.typ.len == 2 and op.typ[1].kind != tyVar: if n[1].kind == nkSym and n[1].sym.kind == skParam and n[1].typ.kind == tyVar: result[1] = genDeref(n[1]) From 14a9929464b9f658155ed429397216736fccc259 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 18 Jul 2023 19:06:21 +0500 Subject: [PATCH 059/347] Fix #22281 (#22289) Respect `--gcc.exe` and similar options when `--genScript:on` is used. --- compiler/extccomp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 832242456a8a..391f158f0ccf 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -580,7 +580,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, compilePattern = joinPath(conf.cCompilerPath, exe) else: - compilePattern = getCompilerExe(conf, c, isCpp) + compilePattern = exe includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)])) From 1aff402998e6c17a3d72a8dc23fb655208d93fcb Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 19 Jul 2023 15:45:28 +0800 Subject: [PATCH 060/347] fixes #6499; disallow built-in procs used as procvars (#22291) --- compiler/sempass2.nim | 1 + tests/errmsgs/t6499.nim | 6 ++++++ tests/system/tmagics.nim | 4 ---- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 tests/errmsgs/t6499.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b58d08a018f6..97471104711b 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1223,6 +1223,7 @@ proc track(tracked: PEffects, n: PNode) = of nkTupleConstr: for i in 0.. Date: Wed, 19 Jul 2023 18:57:58 +0800 Subject: [PATCH 061/347] fixes #22268; fixes `move` codegen (#22288) --- compiler/ccgexprs.nim | 7 +++++++ compiler/liftdestructors.nim | 6 +++--- compiler/lowerings.nim | 13 ++----------- lib/system.nim | 28 ++++++++-------------------- 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 00781a31d80c..2b9f4221f0ab 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2359,6 +2359,13 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d) if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: genAssignment(p, d, a, {}) + var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved) + if op == nil: + resetLoc(p, a) + else: + let addrExp = makeAddr(n[1], p.module.idgen) + let wasMovedCall = newTreeI(nkCall, n.info, newSymNode(op), addrExp) + genCall(p, wasMovedCall, d) else: let flags = if not canMove(p, n[1], d): {needToCopy} else: {} genAssignment(p, d, a, flags) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index eac2323aa933..11d483abb3d9 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -556,7 +556,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add setLenSeqCall(c, t, x, y) forallElements(c, t, body, x, y) of attachedSink: - let moveCall = genBuiltin(c, mMove, "internalMove", x) + let moveCall = genBuiltin(c, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) @@ -589,7 +589,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newHookCall(c, t.assignment, x, y) of attachedSink: # we always inline the move for better performance: - let moveCall = genBuiltin(c, mMove, "internalMove", x) + let moveCall = genBuiltin(c, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) @@ -620,7 +620,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedAsgn, attachedDeepCopy, attachedDup: body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y) of attachedSink: - let moveCall = genBuiltin(c, mMove, "internalMove", x) + let moveCall = genBuiltin(c, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 3f67fc168eac..d70c713a1512 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -66,17 +66,8 @@ proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode = result = newNodeI(nkFastAsgn, le.info, 2) result[0] = le result[1] = newNodeIT(nkCall, ri.info, ri.typ) - if g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: - result[1].add newSymNode(getCompilerProc(g, "internalMove")) - result[1].add ri - result = newTreeI(nkStmtList, le.info, result, - newTree(nkCall, newSymNode( - getSysMagic(g, ri.info, "=wasMoved", mWasMoved)), - ri - )) - else: - result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove)) - result[1].add ri + result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove)) + result[1].add ri proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = assert n.kind == nkVarTuple diff --git a/lib/system.nim b/lib/system.nim index 50debcc89504..858571d61b3d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -151,26 +151,10 @@ proc wasMoved*[T](obj: var T) {.inline, noSideEffect.} = {.cast(raises: []), cast(tags: []).}: `=wasMoved`(obj) -const notJSnotNims = not defined(js) and not defined(nimscript) -const arcLikeMem = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) - -when notJSnotNims and arcLikeMem: - proc internalMove[T](x: var T): T {.magic: "Move", noSideEffect, compilerproc.} = - result = x - - proc move*[T](x: var T): T {.noSideEffect, nodestroy.} = - {.cast(noSideEffect).}: - when nimvm: - result = internalMove(x) - else: - result = internalMove(x) - {.cast(raises: []), cast(tags: []).}: - `=wasMoved`(x) -else: - proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} = - result = x - {.cast(raises: []), cast(tags: []).}: - `=wasMoved`(x) +proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} = + result = x + {.cast(raises: []), cast(tags: []).}: + `=wasMoved`(x) type range*[T]{.magic: "Range".} ## Generic type to construct range types. @@ -369,6 +353,9 @@ proc arrGet[I: Ordinal;T](a: T; i: I): T {. proc arrPut[I: Ordinal;T,S](a: T; i: I; x: S) {.noSideEffect, magic: "ArrPut".} +const arcLikeMem = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc) + + when defined(nimAllowNonVarDestructor) and arcLikeMem: proc `=destroy`*(x: string) {.inline, magic: "Destroy".} = discard @@ -445,6 +432,7 @@ include "system/inclrtl" const NoFakeVars = defined(nimscript) ## `true` if the backend doesn't support \ ## "fake variables" like `var EBADF {.importc.}: cint`. +const notJSnotNims = not defined(js) and not defined(nimscript) when not defined(js) and not defined(nimSeqsV2): type From 0d3bde95f578576d2e84d422d5694ee0e0055cbc Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Wed, 19 Jul 2023 09:04:14 -0400 Subject: [PATCH 062/347] Adding info to manual (#22252) * Adjustments * Moving example * typo * adding code example back and fix terms * Condensing --- doc/manual.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 1c3e668791ef..e3a036f0cad8 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -2642,9 +2642,15 @@ of the argument. 6. Conversion match: `a` is convertible to `f`, possibly via a user defined `converter`. -These matching categories have a priority: An exact match is better than a -literal match and that is better than a generic match etc. In the following, -`count(p, m)` counts the number of matches of the matching category `m` + +There are two major methods of selecting the best matching candidate, namely +counting and disambiguation. Counting takes precedence to disambiguation. In counting, +each parameter is given a category and the number of parameters in each category is counted. +The categories are listed above and are in order of precedence. For example, if +a candidate with one exact match is compared to a candidate with multiple generic matches +and zero exact matches, the candidate with an exact match will win. + +In the following, `count(p, m)` counts the number of matches of the matching category `m` for the routine `p`. A routine `p` matches better than a routine `q` if the following @@ -2662,6 +2668,11 @@ algorithm returns true: return "ambiguous" ``` +When counting is ambiguous, disambiguation begins. Parameters are iterated +by position and these parameter pairs are compared for their type relation. The general goal +of this comparison is to determine which parameter is more specific. The types considered are +not of the inputs from the callsite, but of the competing candidates' parameters. + Some examples: @@ -5470,6 +5481,49 @@ The following example shows how a generic binary tree can be modeled: The `T` is called a `generic type parameter`:idx: or a `type variable`:idx:. + +Generic Procs +--------------- + +Let's consider the anatomy of a generic `proc` to agree on defined terminology. + +```nim +p[T: t](arg1: f): y +``` + +- `p`: Callee symbol +- `[...]`: Generic parameters +- `T: t`: Generic constraint +- `T`: Type variable +- `[T: t](arg1: f): y`: Formal signature +- `arg1: f`: Formal parameter +- `f`: Formal parameter type +- `y`: Formal return type + +The use of the word "formal" here is to denote the symbols as they are defined by the programmer, +not as they may be at compile time contextually. Since generics may be instantiated and +types bound, we have more than one entity to think about when generics are involved. + +The usage of a generic will resolve the formally defined expression into an instance of that +expression bound to only concrete types. This process is called "instantiation". + +Brackets at the site of a generic's formal definition specify the "constraints" as in: + +```nim +type Foo[T] = object +proc p[H;T: Foo[H]](param: T): H +``` + +A constraint definition may have more than one symbol defined by seperating each definition by +a `;`. Notice how `T` is composed of `H` and the return type of `p` is defined as `H`. When this +generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and +the return type of `p` will be bound to the same concrete type used to define `H`. + +Brackets at the site of usage can be used to supply concrete types to instantiate the generic in the same +order that the symbols are defined in the constraint. Alternatively, type bindings may be inferred by the compiler +in some situations, allowing for cleaner code. + + Is operator ----------- From 5ed44e1ec463b68180e17cfe59c8c68d8c55d406 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 19 Jul 2023 21:20:41 +0800 Subject: [PATCH 063/347] fixes #22254; fixes #22253; stricteffects bugs on recursive calls (#22294) --- lib/pure/json.nim | 5 +++-- tests/effects/tstrict_effects3.nim | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 2e448dba708b..fcb9eae41b6b 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -438,7 +438,7 @@ macro `%*`*(x: untyped): untyped = ## `%` for every element. result = toJsonImpl(x) -proc `==`*(a, b: JsonNode): bool {.noSideEffect.} = +proc `==`*(a, b: JsonNode): bool {.noSideEffect, raises: [].} = ## Check two nodes for equality if a.isNil: if b.isNil: return true @@ -458,7 +458,8 @@ proc `==`*(a, b: JsonNode): bool {.noSideEffect.} = of JNull: result = true of JArray: - result = a.elems == b.elems + {.cast(raises: []).}: # bug #19303 + result = a.elems == b.elems of JObject: # we cannot use OrderedTable's equality here as # the order does not matter for equality here. diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim index 027b4647413a..0d98a0343dc6 100644 --- a/tests/effects/tstrict_effects3.nim +++ b/tests/effects/tstrict_effects3.nim @@ -44,3 +44,14 @@ proc fail() = discard f1() f2() +import std/json + +# bug #22254 +proc senri(a, b: seq[JsonNode]) {.raises: [].} = discard a == b + +# bug #22253 +proc serika() {.raises: [].} = discard default(JsonNode) == nil + +senri(@[newJBool(true)], @[newJBool(false)]) +serika() + From c1a82aa5c5ab68dfc2ab6f09779d9ab9bbf3758f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 19 Jul 2023 16:03:26 +0200 Subject: [PATCH 064/347] minor code improvement (#22293) --- compiler/closureiters.nim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 3c5d3991be61..ae4fde0f6a8b 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -1389,7 +1389,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode = discard c.finallys.pop() of nkWhileStmt, nkBlockStmt: - if n.hasYields == false: return n + if not n.hasYields: return n c.blocks.add((n, c.finallys.len)) for i in 0 ..< n.len: result[i] = preprocess(c, n[i]) @@ -1466,9 +1466,10 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: result = ctx.transformStateAssignments(result) result = ctx.wrapIntoStateLoop(result) - # echo "TRANSFORM TO STATES: " - # echo renderTree(result) + when false: + echo "TRANSFORM TO STATES: " + echo renderTree(result) - # echo "exception table:" - # for i, e in ctx.exceptionTable: - # echo i, " -> ", e + echo "exception table:" + for i, e in ctx.exceptionTable: + echo i, " -> ", e From 3f9e16594fb26b78f812094a86d5e269093d8034 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 21 Jul 2023 03:56:04 +1000 Subject: [PATCH 065/347] fix `jsondoc` not getting `showNonExports` flag (#22267) Pass the config down so we can check if the `--showNonExports` flag is used --- compiler/docgen.nim | 17 ++++++++++------- compiler/docgen2.nim | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 958d804b34c2..829d86dfdcb0 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1145,13 +1145,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx if k == skType and nameNode.kind == nkSym: d.types.strTableAdd nameNode.sym -proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = +proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem = if not isVisible(d, nameNode): return var name = getNameEsc(d, nameNode) comm = genRecComment(d, n) r: TSrcGen - initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}) + renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing} + if nonExports: + renderFlags.incl renderNonExportedFields + initTokRender(r, n, renderFlags) result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} if comm != nil: @@ -1536,7 +1539,7 @@ proc finishGenerateDoc*(d: var PDoc) = proc add(d: PDoc; j: JsonItem) = if j.json != nil or j.rst != nil: d.jEntriesPre.add j -proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = +proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool = true) = case n.kind of nkPragma: let doctypeNode = findPragma(n, wDoctype) @@ -1568,14 +1571,14 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = if n[i].kind != nkCommentStmt: # order is always 'type var let const': d.add genJsonItem(d, n[i], n[i][0], - succ(skType, ord(n.kind)-ord(nkTypeSection))) + succ(skType, ord(n.kind)-ord(nkTypeSection)), optShowNonExportedFields in config.globalOptions) of nkStmtList: for i in 0.. Date: Thu, 20 Jul 2023 13:56:54 -0400 Subject: [PATCH 066/347] `infixArgument` fail in `renderer.nim` sometimes (#22264) * fixing minor typo * Adding err msg --- compiler/renderer.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ac4ff2a77e18..b9c3268c4ce4 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1038,7 +1038,7 @@ proc accentedName(g: var TSrcGen, n: PNode) = gsub(g, n) proc infixArgument(g: var TSrcGen, n: PNode, i: int) = - if i < 1 and i > 2: return + if i < 1 or i > 2: return var needsParenthesis = false let nNext = n[i].skipHiddenNodes if nNext.kind == nkInfix: @@ -1382,6 +1382,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = putWithSpace(g, tkColon, ":") gsub(g, n, 1) of nkInfix: + if n.len < 3: + var i = 0 + put(g, tkOpr, "Too few children for nkInfix") + return let oldLineLen = g.lineLen # we cache this because lineLen gets updated below infixArgument(g, n, 1) put(g, tkSpaces, Space) From 91987f8eb56b47bd88c3f27784818bde4fd05ce2 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 21 Jul 2023 11:40:11 +0800 Subject: [PATCH 067/347] fixes #22210; transform return future in try/finally properly (#22249) * wip; fixes #22210; transform return future in try/finally properly * add a test case for #22210 * minor * inserts a needsCompletion flag * uses copyNimNode --- lib/pure/asyncmacro.nim | 57 ++++++++++++++++++++++++++----- tests/async/t22210.nim | 41 ++++++++++++++++++++++ tests/async/t22210_2.nim | 73 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 tests/async/t22210.nim create mode 100644 tests/async/t22210_2.nim diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index e41568b8c1b7..a026e159e855 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -11,6 +11,11 @@ import macros, strutils, asyncfutures +type + Context = ref object + inTry: int + hasRet: bool + # TODO: Ref https://github.com/nim-lang/Nim/issues/5617 # TODO: Add more line infos proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimNode]): NimNode = @@ -63,7 +68,7 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode ) ) -proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode = +proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode = result = node case node.kind of nnkReturnStmt: @@ -72,23 +77,53 @@ proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): Ni # As I've painfully found out, the order here really DOES matter. result.add createFutureVarCompletions(futureVarIdents, node) + ctx.hasRet = true if node[0].kind == nnkEmpty: - result.add newCall(newIdentNode("complete"), retFutureSym, newIdentNode("result")) + if ctx.inTry == 0: + result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, newIdentNode("result")) + else: + result.add newAssignment(needsCompletionSym, newLit(true)) else: - let x = node[0].processBody(retFutureSym, futureVarIdents) + let x = processBody(ctx, node[0], needsCompletionSym, retFutureSym, futureVarIdents) if x.kind == nnkYieldStmt: result.add x + elif ctx.inTry == 0: + result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, x) else: - result.add newCall(newIdentNode("complete"), retFutureSym, x) + result.add newAssignment(newIdentNode("result"), x) + result.add newAssignment(needsCompletionSym, newLit(true)) result.add newNimNode(nnkReturnStmt, node).add(newNilLit()) return # Don't process the children of this return stmt of RoutineNodes-{nnkTemplateDef}: # skip all the nested procedure definitions return - else: discard - - for i in 0 ..< result.len: - result[i] = processBody(result[i], retFutureSym, futureVarIdents) + of nnkTryStmt: + if result[^1].kind == nnkFinally: + inc ctx.inTry + result[0] = processBody(ctx, result[0], needsCompletionSym, retFutureSym, futureVarIdents) + dec ctx.inTry + for i in 1 ..< result.len: + result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents) + if ctx.inTry == 0 and ctx.hasRet: + let finallyNode = copyNimNode(result[^1]) + let stmtNode = newNimNode(nnkStmtList) + for child in result[^1]: + stmtNode.add child + stmtNode.add newIfStmt( + ( needsCompletionSym, + newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, + newIdentNode("result") + ) + ) + ) + finallyNode.add stmtNode + result[^1] = finallyNode + else: + for i in 0 ..< result.len: + result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents) + else: + for i in 0 ..< result.len: + result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents) # echo result.repr @@ -213,7 +248,9 @@ proc asyncSingleProc(prc: NimNode): NimNode = # -> # -> complete(retFuture, result) var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)") - var procBody = prc.body.processBody(retFutureSym, futureVarIdents) + var needsCompletionSym = genSym(nskVar, "needsCompletion") + var ctx = Context() + var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutureSym, futureVarIdents) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: # fix #13899, defer should not escape its original scope @@ -234,6 +271,8 @@ proc asyncSingleProc(prc: NimNode): NimNode = else: var `resultIdent`: Future[void] {.pop.} + + var `needsCompletionSym` = false procBody.add quote do: complete(`retFutureSym`, `resultIdent`) diff --git a/tests/async/t22210.nim b/tests/async/t22210.nim new file mode 100644 index 000000000000..fcf939472599 --- /dev/null +++ b/tests/async/t22210.nim @@ -0,0 +1,41 @@ +discard """ +output: ''' +stage 1 +stage 2 +stage 3 +(status: 200, data: "SOMEDATA") +''' +""" + +import std/asyncdispatch + + +# bug #22210 +type + ClientResponse = object + status*: int + data*: string + +proc subFoo1(): Future[int] {.async.} = + await sleepAsync(100) + return 200 + +proc subFoo2(): Future[string] {.async.} = + await sleepAsync(100) + return "SOMEDATA" + +proc testFoo(): Future[ClientResponse] {.async.} = + try: + let status = await subFoo1() + doAssert(status == 200) + let data = await subFoo2() + return ClientResponse(status: status, data: data) + finally: + echo "stage 1" + await sleepAsync(100) + echo "stage 2" + await sleepAsync(200) + echo "stage 3" + +when isMainModule: + echo waitFor testFoo() \ No newline at end of file diff --git a/tests/async/t22210_2.nim b/tests/async/t22210_2.nim new file mode 100644 index 000000000000..9db664a32dd2 --- /dev/null +++ b/tests/async/t22210_2.nim @@ -0,0 +1,73 @@ +import std/asyncdispatch + + +# bug #22210 +type + ClientResponse = object + status*: int + data*: string + +proc subFoo1(): Future[int] {.async.} = + await sleepAsync(100) + return 200 + +proc subFoo2(): Future[string] {.async.} = + await sleepAsync(100) + return "SOMEDATA" + + +proc testFoo2(): Future[ClientResponse] {.async.} = + var flag = 0 + try: + let status = await subFoo1() + doAssert(status == 200) + let data = await subFoo2() + result = ClientResponse(status: status, data: data) + finally: + inc flag + await sleepAsync(100) + inc flag + await sleepAsync(200) + inc flag + doAssert flag == 3 + +discard waitFor testFoo2() + +proc testFoo3(): Future[ClientResponse] {.async.} = + var flag = 0 + try: + let status = await subFoo1() + doAssert(status == 200) + let data = await subFoo2() + if false: + return ClientResponse(status: status, data: data) + finally: + inc flag + await sleepAsync(100) + inc flag + await sleepAsync(200) + inc flag + doAssert flag == 3 + +discard waitFor testFoo3() + + +proc testFoo4(): Future[ClientResponse] {.async.} = + var flag = 0 + try: + let status = await subFoo1() + doAssert(status == 200) + let data = await subFoo2() + if status == 200: + return ClientResponse(status: status, data: data) + else: + return ClientResponse() + finally: + inc flag + await sleepAsync(100) + inc flag + await sleepAsync(200) + inc flag + doAssert flag == 3 + +discard waitFor testFoo4() From 993fcf5bdac32964237b29e279ecf839095ac609 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 22 Jul 2023 11:31:01 +0800 Subject: [PATCH 068/347] fixes CI; disable SSL tests on osx for now (#22304) * test CI * disable osx --- tests/async/tasyncssl.nim | 1 + tests/misc/trunner_special.nim | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index 222aaa3a183a..57de3271d5c1 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -1,5 +1,6 @@ discard """ cmd: "nim $target --hints:on --define:ssl $options $file" + disabled: osx """ import asyncdispatch, asyncnet, net, strutils diff --git a/tests/misc/trunner_special.nim b/tests/misc/trunner_special.nim index 50a2e4d5adad..e1381072261c 100644 --- a/tests/misc/trunner_special.nim +++ b/tests/misc/trunner_special.nim @@ -1,6 +1,7 @@ discard """ targets: "c cpp" joinable: false + disabled: osx """ #[ From b02c1dd6ca96548b47d978f96278c67bf59e9d9e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 22 Jul 2023 12:37:27 +0800 Subject: [PATCH 069/347] fixes #22297; return in the finally in the closure iterators (#22300) ref #22297; return in the finally in the closure iterators --- compiler/closureiters.nim | 4 +++- tests/closure/tclosure.nim | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index ae4fde0f6a8b..87c5b795ebac 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -855,7 +855,9 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode = case n.kind of nkReturnStmt: # We're somewhere in try, transform to finally unrolling - assert(ctx.nearestFinally != 0) + if ctx.nearestFinally == 0: + # return is within the finally + return result = newNodeI(nkStmtList, n.info) diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim index fa1f79ffeb32..401a71d4009d 100644 --- a/tests/closure/tclosure.nim +++ b/tests/closure/tclosure.nim @@ -491,3 +491,14 @@ block tnoclosure: row = zip(row & @[0], @[0] & row).mapIt(it[0] + it[1]) echo row pascal(10) + +block: # bug #22297 + iterator f: int {.closure.} = + try: + yield 12 + finally: + return 14 + + let s = f + doAssert s() == 12 + doAssert s() == 14 From 3ebe24977ce93ca3c347550c69dbfa7c9a7db507 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sat, 22 Jul 2023 19:09:39 +0200 Subject: [PATCH 070/347] Open scope for defer (#22315) Co-authored-by: SirOlaf <> --- compiler/semexprs.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1d917f00d9f3..c6be3e833c35 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -3282,7 +3282,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType of nkDefer: if c.currentScope == c.topLevelScope: localError(c.config, n.info, "defer statement not supported at top level") + openScope(c) n[0] = semExpr(c, n[0]) + closeScope(c) if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]): localError(c.config, n.info, "'defer' takes a 'void' expression") #localError(c.config, n.info, errGenerated, "'defer' not allowed in this context") From 576f4a73483a5d3b4c600f6d3d3c85394ffb43ee Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sat, 22 Jul 2023 19:10:12 +0200 Subject: [PATCH 071/347] Fix doc comment rendering for concepts (#22312) --- compiler/docgen.nim | 2 +- tests/concepts/t20237.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 829d86dfdcb0..5120b522381d 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -440,7 +440,7 @@ proc genRecCommentAux(d: PDoc, n: PNode): PRstNode = if n == nil: return nil result = genComment(d, n) if result == nil: - if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, + if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkTypeClassTy, nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}: # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}: for i in 0.. Date: Sat, 22 Jul 2023 21:11:08 +0200 Subject: [PATCH 072/347] Add test for #22309 (#22316) --- tests/defer/t22309.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/defer/t22309.nim diff --git a/tests/defer/t22309.nim b/tests/defer/t22309.nim new file mode 100644 index 000000000000..34ca4843b23c --- /dev/null +++ b/tests/defer/t22309.nim @@ -0,0 +1,11 @@ +block: + defer: + let a = 42 + doAssert not declared(a) + +proc lol() = + defer: + let a = 42 + doAssert not declared(a) + +lol() From e2ea9140ace56d9ed2d40cef5c63b4a271788214 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:11:49 +0200 Subject: [PATCH 073/347] Document `cast` zeroing memory (#22313) --- doc/manual.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index e3a036f0cad8..45eb8fef562d 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -2643,11 +2643,11 @@ of the argument. defined `converter`. -There are two major methods of selecting the best matching candidate, namely +There are two major methods of selecting the best matching candidate, namely counting and disambiguation. Counting takes precedence to disambiguation. In counting, each parameter is given a category and the number of parameters in each category is counted. The categories are listed above and are in order of precedence. For example, if -a candidate with one exact match is compared to a candidate with multiple generic matches +a candidate with one exact match is compared to a candidate with multiple generic matches and zero exact matches, the candidate with an exact match will win. In the following, `count(p, m)` counts the number of matches of the matching category `m` @@ -2668,9 +2668,9 @@ algorithm returns true: return "ambiguous" ``` -When counting is ambiguous, disambiguation begins. Parameters are iterated +When counting is ambiguous, disambiguation begins. Parameters are iterated by position and these parameter pairs are compared for their type relation. The general goal -of this comparison is to determine which parameter is more specific. The types considered are +of this comparison is to determine which parameter is more specific. The types considered are not of the inputs from the callsite, but of the competing candidates' parameters. @@ -3760,6 +3760,9 @@ bit pattern of the data being cast (aside from that the size of the target type may differ from the source type). Casting resembles *type punning* in other languages or C++'s `reinterpret_cast`:cpp: and `bit_cast`:cpp: features. +If the size of the target type is larger than the size of the source type, +the remaining memory is zeroed. + The addr operator ----------------- The `addr` operator returns the address of an l-value. If the type of the @@ -5500,7 +5503,7 @@ p[T: t](arg1: f): y - `f`: Formal parameter type - `y`: Formal return type -The use of the word "formal" here is to denote the symbols as they are defined by the programmer, +The use of the word "formal" here is to denote the symbols as they are defined by the programmer, not as they may be at compile time contextually. Since generics may be instantiated and types bound, we have more than one entity to think about when generics are involved. @@ -5514,9 +5517,9 @@ type Foo[T] = object proc p[H;T: Foo[H]](param: T): H ``` -A constraint definition may have more than one symbol defined by seperating each definition by -a `;`. Notice how `T` is composed of `H` and the return type of `p` is defined as `H`. When this -generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and +A constraint definition may have more than one symbol defined by seperating each definition by +a `;`. Notice how `T` is composed of `H` and the return type of `p` is defined as `H`. When this +generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and the return type of `p` will be bound to the same concrete type used to define `H`. Brackets at the site of usage can be used to supply concrete types to instantiate the generic in the same @@ -8538,18 +8541,18 @@ The `bycopy` pragma can be applied to an object or tuple type or a proc param. I x, y, z: float ``` -The Nim compiler automatically determines whether a parameter is passed by value or -by reference based on the parameter type's size. If a parameter must be passed by value -or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas. +The Nim compiler automatically determines whether a parameter is passed by value or +by reference based on the parameter type's size. If a parameter must be passed by value +or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas. Notice params marked as `byref` takes precedence over types marked as `bycopy`. Byref pragma ------------ The `byref` pragma can be applied to an object or tuple type or a proc param. -When applied to a type it instructs the compiler to pass the type by reference -(hidden pointer) to procs. When applied to a param it will take precedence, even -if the the type was marked as `bycopy`. When using the Cpp backend, params marked +When applied to a type it instructs the compiler to pass the type by reference +(hidden pointer) to procs. When applied to a param it will take precedence, even +if the the type was marked as `bycopy`. When using the Cpp backend, params marked as byref will translate to cpp references `&`. Varargs pragma From b10d3cd98d66b9fff20f9bf37d454c07ebbd42b2 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:13:23 +0200 Subject: [PATCH 074/347] Update 2.0 changelog (#22311) --- changelogs/changelog_2_0_0.md | 73 +++++++------- changelogs/changelog_2_0_0_details.md | 137 ++++++++++++++------------ 2 files changed, 110 insertions(+), 100 deletions(-) diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index 85a0753d0045..d954c774ef44 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -2,7 +2,7 @@ Version 2.0 is a big milestone with too many changes to list them all here. -For a full list see [details](changelog_2_0_0_details.html) +For a full list see [details](changelog_2_0_0_details.html). ## New features @@ -31,10 +31,9 @@ For example, code like the following now compiles: let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] ``` - ### Forbidden Tags -[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition +[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types. For example: @@ -53,11 +52,11 @@ proc no_IO_please() {.forbids: [IO].} = ``` -### New standard libraries +### New standard library modules The famous `os` module got an overhaul. Several of its features are available under a new interface that introduces a `Path` abstraction. A `Path` is -a `distinct string` and improves the type safety when dealing with paths, files +a `distinct string`, which improves the type safety when dealing with paths, files and directories. Use: @@ -71,7 +70,6 @@ Use: - `std/appdirs` for accessing configuration/home/temp directories. - `std/cmdline` for reading command line parameters. - ### Consistent underscore handling The underscore identifier (`_`) is now generally not added to scope when @@ -127,47 +125,49 @@ old behavior is currently still supported with the command line option ## Docgen improvements -`Markdown` is now default markup language of doc comments (instead -of legacy `RstMarkdown` mode). In this release we begin to separate +`Markdown` is now the default markup language of doc comments (instead +of the legacy `RstMarkdown` mode). In this release we begin to separate RST and Markdown features to better follow specification of each language, with the focus on Markdown development. +See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html). -* So we added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to - select the markup language mode in the doc comments of current `.nim` +* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to + select the markup language mode in the doc comments of the current `.nim` file for processing by `nim doc`: 1. `Markdown` (default) is basically CommonMark (standard Markdown) + some Pandoc Markdown features + some RST features that are missing in our current implementation of CommonMark and Pandoc Markdown. - 2. `RST` closely follows RST spec with few additional Nim features. + 2. `RST` closely follows the RST spec with few additional Nim features. 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which is kept for the sake of compatibility and ease of migration. -* We added separate `md2html` and `rst2html` commands for processing - standalone `.md` and `.rst` files respectively (and also `md2tex/rst2tex`). +* Added separate `md2html` and `rst2html` commands for processing + standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`). -* We added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. -* The docgen now supports concise syntax for referencing Nim symbols: +* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. +* Docgen now supports concise syntax for referencing Nim symbols: instead of specifying HTML anchors directly one can use original Nim symbol declarations (adding the aforementioned link brackets `[...]` around them). -* To use this feature across modules a new `importdoc` directive was added. - Using this feature for referencing also helps to ensure that links - (inside one module or the whole project) are not broken. -* We added support for RST & Markdown quote blocks (blocks starting from `>`). -* We added a popular Markdown definition lists extension. -* Markdown indented code blocks (blocks indented by >= 4 spaces) have been added. -* We added syntax for additional parameters to Markdown code blocks: + * To use this feature across modules, a new `importdoc` directive was added. + Using this feature for referencing also helps to ensure that links + (inside one module or the whole project) are not broken. +* Added support for RST & Markdown quote blocks (blocks starting with `>`). +* Added a popular Markdown definition lists extension. +* Added Markdown indented code blocks (blocks indented by >= 4 spaces). +* Added syntax for additional parameters to Markdown code blocks: ```nim test="nim c $1" ... ``` + ## C++ interop enhancements -Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma -Now one can define constructors and virtual that maps to C++ constructors and virtual methods. Allowing one to further customize -the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so it works on types. +Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma. +Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize +the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types. It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces. @@ -181,6 +181,7 @@ struct Base { someValue = inValue; }; }; + class IPrinter { public: virtual void print() = 0; @@ -200,11 +201,11 @@ const objTemplate = """ }; """; -type NimChild {.codegenDecl:objTemplate .} = object of Base +type NimChild {.codegenDecl: objTemplate .} = object of Base -proc makeNimChild(val: int32): NimChild {.constructor:"NimClass('1 #1) : Base(#1)".} = +proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} = echo "It calls the base constructor passing " & $this.someValue - this.someValue = val * 2 #notice how we can access to this inside the constructor. it's of the type ptr NimChild + this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`. proc print*(self: NimChild) {.virtual.} = echo "Some value is " & $self.someValue @@ -223,7 +224,7 @@ Some value is 20 ## ARC/ORC refinements -With the release 2.0 the ARC/ORC model got refined once again and is now finally complete: +With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete: 1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable. 2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations. @@ -238,13 +239,13 @@ providing a stable ABI it is important not to lose any efficiency in the calling ## Tool changes -- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. - nimgrep now offers the option `--inContext` (and `--notInContext`), which - allows to filter only matches with context block containing a given pattern. + allows to filter only matches with the context block containing a given pattern. - nimgrep: names of options containing "include/exclude" are deprecated, e.g. instead of `--includeFile` and `--excludeFile` we have `--filename` and `--notFilename` respectively. - Also the semantics become consistent for such positive/negative filters. + Also, the semantics are now consistent for such positive/negative filters. - Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions. @@ -278,7 +279,7 @@ block maybePerformB: ### Strict funcs -The definition of "strictFuncs" was changed. +The definition of `"strictFuncs"` was changed. The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter". The new definition is: "A store to a ref/ptr deref is forbidden." @@ -312,11 +313,9 @@ func create(s: string): Node = ``` - - ### Standard library -Several Standard libraries have been moved to nimble packages, use `nimble` or `atlas` to install them: +Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them: - `std/punycode` => `punycode` - `std/asyncftpclient` => `asyncftpclient` @@ -328,4 +327,4 @@ Several Standard libraries have been moved to nimble packages, use `nimble` or ` - `std/db_odbc` => `db_connector/db_odbc` - `std/md5` => `checksums/md5` - `std/sha1` => `checksums/sha1` - +- `std/sums` => `sums` diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md index 0cdf3d326be7..8f9e7afd0ba4 100644 --- a/changelogs/changelog_2_0_0_details.md +++ b/changelogs/changelog_2_0_0_details.md @@ -2,7 +2,13 @@ ## Changes affecting backward compatibility -- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behaviors. Previously it raised `ValueError`. + +- ORC is now the default memory management strategy. Use + `--mm:refc` for a transition period. + +- The `threads:on` option is now the default. + +- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`. - `addr` is now available for all addressable locations, `unsafeAddr` is now deprecated and an alias for `addr`. @@ -25,7 +31,7 @@ - Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated symbols in the `system` module: - - Aliases with `Error` suffix to exception types that have a `Defect` suffix + - Aliases with an `Error` suffix to exception types that have a `Defect` suffix (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)): `ArithmeticError`, `DivByZeroError`, `OverflowError`, `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, @@ -40,21 +46,19 @@ `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` - Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in - in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command "nimble install threading" and import `threading/channels`. + in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`. -- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. +- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore. -- Enabling `-d:nimPreviewProcConversion`, `proc` does not support conversion to - `pointer`. `cast` may be used instead. +- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to + `pointer` anymore. `cast` may be used instead. - The `gc:v2` option is removed. - The `mainmodule` and `m` options are removed. -- The `threads:on` option is now the default. - -- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via - `experimental:flexibleOptionalParams`. +- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405)) + are now opt-in via `experimental:flexibleOptionalParams`. - Automatic dereferencing (experimental feature) is removed. @@ -81,7 +85,8 @@ var x: Foo = Foo(nil) ``` - Removed two type pragma syntaxes deprecated since 0.20, namely - `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. + `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead, + use `type Foo[T] {.final.} = object`. - `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. @@ -96,16 +101,13 @@ - Lock levels are deprecated, now a noop. -- ORC is now the default memory management strategy. Use - `--mm:refc` for a transition period. - - `strictEffects` are no longer experimental. Use `legacy:laxEffects` to keep backward compatibility. - The `gorge`/`staticExec` calls will now return a descriptive message in the output - if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. + if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`. -- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. +- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning. This warning will become an error in future versions! Use a `cast` operation like `cast[cstring](x)` instead. @@ -129,14 +131,21 @@ - `std/db_odbc` => `db_connector/db_odbc` - `std/md5` => `checksums/md5` - `std/sha1` => `checksums/sha1` + - `std/sums` => `std/sums` - Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. -- When `--warning[BareExcept]:on` is enabled, if no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. +- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning: + ```nim + try: + discard + except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept] + discard + ``` -- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. +- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection. - 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 @@ -212,7 +221,7 @@ - The `proc` and `iterator` type classes now accept a calling convention pragma (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator - types. Previously pragmas were parsed but discarded if no parameter list + types. Previously, pragmas were parsed but discarded if no parameter list was given. This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with @@ -225,14 +234,14 @@ - Signed integer literals in `set` literals now default to a range type of `0..255` instead of `0..65535` (the maximum size of sets). -- Case statements with else branches put before elif/of branches in macros +- `case` statements with `else` branches put before `elif`/`of` branches in macros are rejected with "invalid order of case branches". - Destructors now default to `.raises: []` (i.e. destructors must not raise unlisted exceptions) and explicitly raising destructors are implementation defined behavior. -- The very old, undocumented deprecated pragma statement syntax for +- The very old, undocumented `deprecated` pragma statement syntax for deprecated aliases is now a no-op. The regular deprecated pragma syntax is generally sufficient instead. @@ -254,7 +263,7 @@ declared when they are not available on the backend. Previously it would call `doAssert false` at runtime despite the condition being checkable at compile-time. -- Custom destructors now supports non-var parameters, e.g. `proc =destroy[T: object](x: T)` is valid. `proc =destroy[T: object](x: var T)` is deprecated. +- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated. - Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly. @@ -263,19 +272,19 @@ [//]: # "Changes:" - OpenSSL 3 is now supported. - `macros.parseExpr` and `macros.parseStmt` now accept an optional - filename argument for more informative errors. -- Module `colors` expanded with missing colors from the CSS color standard. + `filename` argument for more informative errors. +- The `colors` module is expanded with missing colors from the CSS color standard. `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. - Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). - The `md5` module now works at compile time and in JavaScript. -- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. +- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables. - `strutils.find` now uses and defaults to `last = -1` for whole string searches, making limiting it to just the first char (`last = 0`) valid. -- `strutils.split` and `strutils.rsplit` now return a source string as a single element for an empty separator. +- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator. - `random.rand` now works with `Ordinal`s. - Undeprecated `os.isvalidfilename`. -- `std/oids` now uses `int64` to store time internally (before it was int32). -- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. +- `std/oids` now uses `int64` to store time internally (before, it was int32). +- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`. - `std/uri.Uri.isIpv6` is now exported. - `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. @@ -283,9 +292,9 @@ - `std/jsfetch.newFetchOptions` now has default values for all parameters. - `std/jsformdata` now accepts the `Blob` data type. -- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). +- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433). -- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on Linux `getrandom` syscall. +- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall. This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. @@ -301,19 +310,20 @@ $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall ``` - This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (sysrand in particular). + This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular). [//]: # "Additions:" - Added ISO 8601 week date utilities in `times`: - Added `IsoWeekRange`, a range type for weeks in a week-based year. - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. - - Added a `initDateTime` overload to create a datetime from an ISO week date. + - Added an `initDateTime` overload to create a `DateTime` from an ISO week date. - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. -- Added new modules which were part of `std/os`: - - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. +- Added new modules which were previously part of `std/os`: + - Added `std/oserrors` for OS error reporting. + - Added `std/envvars` for environment variables handling. - Added `std/cmdline` for reading command line parameters. + - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. - Added `sep` parameter in `std/uri` to specify the query separator. - Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. - Added `complex.sgn` for obtaining the phase of complex numbers. @@ -322,14 +332,13 @@ `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, `toggleAttribute`, and `matches` to `std/dom`. -- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) -- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 -- Added `openArray[char]` overloads for `std/parseutils` allowing for more code reuse. -- Added `openArray[char]` overloads for `std/unicode` allowing for more code reuse. -- Added `safe` parameter to `base64.encodeMime`. +- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices). +- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460). +- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse. +- Added a `safe` parameter to `base64.encodeMime`. - Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. - Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences. -- `std/jscore` for JavaScript targets: +- `std/jscore` for the JavaScript target: + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask). + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`. @@ -339,7 +348,6 @@ [//]: # "Deprecations:" - Deprecated `selfExe` for Nimscript. -- Deprecated `std/sums`. - Deprecated `std/base64.encode` for collections of arbitrary integer element type. Now only `byte` and `char` are supported. @@ -354,7 +362,7 @@ - Removed deprecated `jsre.test` and `jsre.toString`. - Removed deprecated `math.c_frexp`. - Removed deprecated `` httpcore.`==` ``. -- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types. - Removed deprecated `osproc.poDemon`, symbol with typo. - Removed deprecated `tables.rightSize`. @@ -364,7 +372,7 @@ ## Language changes -- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma +- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types. - [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. @@ -375,7 +383,7 @@ - Compile-time define changes: - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. In the command line, this is defined as `-d:a.b.c`. Older versions can - use accents as in ``defined(`a.b.c`)`` to access such defines. + use backticks as in ``defined(`a.b.c`)`` to access such defines. - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) now support a string argument for qualified define names. @@ -408,11 +416,13 @@ ```nim import macros + macro multiply(amount: static int, s: untyped): untyped = let name = $s[0].basename result = newNimNode(nnkTypeSection) for i in 1 .. amount: result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) + type Foo = object Bar {.multiply: 3.} = object @@ -446,7 +456,7 @@ - IBM Z architecture and macOS m1 arm64 architecture are supported. -- `=wasMoved` can be overridden by users. +- `=wasMoved` can now 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 @@ -488,43 +498,44 @@ related functions produced on the backend. This prevents conflicts with other Nim static libraries. -- When compiling for Release the flag `-fno-math-errno` is used for GCC. +- When compiling for release, the flag `-fno-math-errno` is used for GCC. - Removed deprecated `LineTooLong` hint. -- Line numbers and filenames of source files work correctly inside templates for JavaScript targets. +- Line numbers and file names of source files work correctly inside templates for JavaScript targets. -- Removed support for LCC (Local C), Pelles C, Digital Mars, Watcom compilers. +- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers. ## Docgen -- `Markdown` is now default markup language of doc comments (instead - of legacy `RstMarkdown` mode). In this release we begin to separate +- `Markdown` is now the default markup language of doc comments (instead + of the legacy `RstMarkdown` mode). In this release we begin to separate RST and Markdown features to better follow specification of each language, with the focus on Markdown development. + See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html). - * So we add `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to - select the markup language mode in the doc comments of current `.nim` + * Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to + select the markup language mode in the doc comments of the current `.nim` file for processing by `nim doc`: 1. `Markdown` (default) is basically CommonMark (standard Markdown) + some Pandoc Markdown features + some RST features that are missing in our current implementation of CommonMark and Pandoc Markdown. - 2. `RST` closely follows RST spec with few additional Nim features. + 2. `RST` closely follows the RST spec with few additional Nim features. 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which is kept for the sake of compatibility and ease of migration. - * and we add separate `md2html` and `rst2html` commands for processing - standalone `.md` and `.rst` files respectively (and also `md2tex/rst2tex`). + * Added separate `md2html` and `rst2html` commands for processing + standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`). - Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. - Docgen now supports concise syntax for referencing Nim symbols: instead of specifying HTML anchors directly one can use original Nim symbol declarations (adding the aforementioned link brackets `[...]` around them). - * to use this feature across modules a new `importdoc` directive is added. - Using this feature for referencing also helps to ensure that links - (inside one module or the whole project) are not broken. -- Added support for RST & Markdown quote blocks (blocks starting from `>`). + * To use this feature across modules, a new `importdoc` directive was added. + Using this feature for referencing also helps to ensure that links + (inside one module or the whole project) are not broken. +- Added support for RST & Markdown quote blocks (blocks starting with `>`). - Added a popular Markdown definition lists extension. - Added Markdown indented code blocks (blocks indented by >= 4 spaces). - Added syntax for additional parameters to Markdown code blocks: @@ -535,11 +546,11 @@ ## Tool changes -- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. - nimgrep added the option `--inContext` (and `--notInContext`), which - allows to filter only matches with context block containing a given pattern. + allows to filter only matches with the context block containing a given pattern. - nimgrep: names of options containing "include/exclude" are deprecated, e.g. instead of `--includeFile` and `--excludeFile` we have `--filename` and `--notFilename` respectively. - Also the semantics become consistent for such positive/negative filters. + Also the semantics are now consistent for such positive/negative filters. - koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice. From 62869a5c68e4dd91e00ee77b039f0175482ef4fa Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sat, 22 Jul 2023 21:13:55 +0200 Subject: [PATCH 075/347] Check try block for endsInNoReturn (#22314) Co-authored-by: SirOlaf <> --- compiler/semstmts.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 836be6e4a998..5c1a363b4d37 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -235,8 +235,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) var typ = commonTypeBegin var expectedType = expectedType n[0] = semExprBranchScope(c, n[0], expectedType) - typ = commonType(c, typ, n[0].typ) if not endsInNoReturn(n[0]): + typ = commonType(c, typ, n[0].typ) expectedType = typ var last = n.len - 1 @@ -312,7 +312,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) result.typ = c.enforceVoidContext else: if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags) - n[0] = fitNode(c, typ, n[0], n[0].info) + if not endsInNoReturn(n[0]): + n[0] = fitNode(c, typ, n[0], n[0].info) for i in 1..last: var it = n[i] let j = it.len-1 From be1844541c87a132ca076d8a8f741bec01825ba1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 23 Jul 2023 13:39:58 +0200 Subject: [PATCH 076/347] =?UTF-8?q?implemented=20'push=20quirky'=20switch?= =?UTF-8?q?=20for=20fine=20grained=20control=20over=20the=20ex=E2=80=A6=20?= =?UTF-8?q?(#22318)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * implemented 'push quirky' switch for fine grained control over the exception handling overhead * documentation --- changelogs/changelog_2_0_0_details.md | 3 + compiler/ccgstmts.nim | 7 ++- compiler/cgen.nim | 10 ++-- compiler/cgendata.nim | 18 +++--- compiler/condsyms.nim | 1 + compiler/options.nim | 1 + compiler/pragmas.nim | 7 ++- compiler/wordrecg.nim | 1 + doc/manual_experimental.md | 81 +++++++++++++++++++++++---- doc/nimc.md | 4 ++ 10 files changed, 102 insertions(+), 31 deletions(-) diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md index 8f9e7afd0ba4..e3895639ad78 100644 --- a/changelogs/changelog_2_0_0_details.md +++ b/changelogs/changelog_2_0_0_details.md @@ -458,6 +458,9 @@ - `=wasMoved` can now be overridden by users. +- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code + generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care. + - 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. diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f536a82c1380..3197389814c3 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -315,7 +315,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var targetProc = p var valueAsRope = "" potentialValueInit(p, v, value, valueAsRope) - if sfGlobal in v.flags: + if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and value.kind == nkEmpty and v.loc.flags * {lfHeader, lfNoDecl} != {}: @@ -1050,7 +1050,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = expr(p, t[0], d) endBlock(p) - # First pass: handle Nim based exceptions: + # First pass: handle Nim based exceptions: lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1]) genRestoreFrameAfterException(p) # an unhandled exception happened! @@ -1308,7 +1308,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", + [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0450625fcd3f..ed149ed0e515 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -619,7 +619,7 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var) # and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used -proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = +proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = let s = n.sym if s.constraint.isNil: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: @@ -640,7 +640,7 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = else: decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) -proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) +proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) = let s = vn.sym @@ -701,7 +701,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: decl.addf(" $1;$n", [s.loc.r]) - + p.module.s[cfsVars].add(decl) if p.withinLoop > 0 and value == "": # fixes tests/run/tzeroarray: @@ -1134,7 +1134,7 @@ proc getProcTypeCast(m: BModule, prc: PSym): Rope = proc genProcBody(p: BProc; procBody: PNode) = genStmts(p, procBody) # modifies p.locals, p.init, etc. - if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}: + if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}: p.flags.incl nimErrorFlagDeclared p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", [])) p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", [])) @@ -1178,7 +1178,7 @@ proc genProcAux*(m: BModule, prc: PSym) = initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) elif sfConstructor in prc.flags: - fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) + fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) else: fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ[0]) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e1309e0fdd77..4d15cf131b89 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -193,15 +193,15 @@ proc initBlock*(): TBlock = result.sections[i] = newRopeAppender() proc newProc*(prc: PSym, module: BModule): BProc = - new(result) - result.prc = prc - result.module = module - result.options = if prc != nil: prc.options - else: module.config.options - result.blocks = @[initBlock()] - result.nestedTryStmts = @[] - result.finallySafePoints = @[] - result.sigConflicts = initCountTable[string]() + result = BProc( + prc: prc, + module: module, + options: if prc != nil: prc.options + else: module.config.options, + blocks: @[initBlock()], + sigConflicts: initCountTable[string]()) + if optQuirky in result.options: + result.flags = {nimErrorFlagDisabled} proc newModuleList*(g: ModuleGraph): BModuleList = BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](), diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 12634248c397..c680504494ca 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -156,3 +156,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasChecksums") defineSymbol("nimHasSendable") defineSymbol("nimAllowNonVarDestructor") + defineSymbol("nimHasQuirky") diff --git a/compiler/options.nim b/compiler/options.nim index d3cf71d4f6ff..8286a575df74 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -49,6 +49,7 @@ type # please make sure we have under 32 options optSinkInference # 'sink T' inference optCursorInference optImportHidden + optQuirky TOptions* = set[TOption] TGlobalOption* = enum diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 9e4a0052dd5d..0d95f596c519 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -34,7 +34,7 @@ const wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, - wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual} + wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, @@ -405,6 +405,7 @@ proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} = of wImplicitStatic: {optImplicitStatic} of wPatterns, wTrMacros: {optTrMacros} of wSinkInference: {optSinkInference} + of wQuirky: {optQuirky} else: {} proc processExperimental(c: PContext; n: PNode) = @@ -1273,12 +1274,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragmaProposition(c, it) of wEnsures: pragmaEnsures(c, it) - of wEnforceNoRaises: + of wEnforceNoRaises, wQuirky: sym.flags.incl sfNeverRaises of wSystemRaisesDefect: sym.flags.incl sfSystemRaisesDefect of wVirtual: - processVirtual(c, it, sym) + processVirtual(c, it, sym) else: invalidPragma(c, it) elif comesFromPush and whichKeyword(ident) != wInvalid: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 21b09707530b..f784f0a75455 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -89,6 +89,7 @@ type wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wSystemRaisesDefect = "systemRaisesDefect", wRedefine = "redefine", wCallsite = "callsite", + wQuirky = "quirky", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 54354f92b9f1..602ca46a588d 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2009,6 +2009,65 @@ The field is within a `case` section of an `object`. is solid and it is expected that eventually this mode becomes the default in later versions. +Quirky routines +=============== + +The default code generation strategy of exceptions under the ARC/ORC model is the so called +`--exceptions:goto` implementation. This implementation inserts a check after every call that +can potentially raise an exception. A typical instruction sequence for this on +for a x86 64 bit machine looks like: + + ``` + cmp DWORD PTR [rbx], 0 + je .L1 + ``` + +This is a memory fetch followed by jump. (An ideal implementation would +use the carry flag and a single instruction like ``jc .L1``.) + +This overhead might not be desired and depending on the sematics of the routine may not be required +either. +So it can be disabled via a `.quirky` annotation: + + ```nim + proc wontRaise(x: int) {.quirky.} = + if x != 0: + # because of `quirky` this will continue even if `write` raised an IO exception: + write x + wontRaise(x-1) + + wontRaise 10 + + ``` + +If the used exception model is not `--exceptions:goto` then the `quirky` pragma has no effect and is +ignored. + +The `quirky` pragma can also be be pushed in order to affect a group of routines and whether +the compiler supports the pragma can be checked with `defined(nimHasQuirky)`: + + ```nim + when defined(nimHasQuirky): + {.push quirky: on.} + + proc doRaise() = raise newException(ValueError, "") + + proc f(): string = "abc" + + proc q(cond: bool) = + if cond: + doRaise() + echo f() + + q(true) + + when defined(nimHasQuirky): + {.pop.} + ``` + +**Warning**: The `quirky` pragma only affects code generation, no check for validity is performed! + + Threading under ARC/ORC ======================= @@ -2141,13 +2200,13 @@ Here's an example of how to use the virtual pragma: ```nim proc newCpp*[T](): ptr T {.importcpp: "new '*0()".} -type +type Foo = object of RootObj FooPtr = ptr Foo Boo = object of Foo BooPtr = ptr Boo -proc salute(self: FooPtr) {.virtual.} = +proc salute(self: FooPtr) {.virtual.} = echo "hello foo" proc salute(self: BooPtr) {.virtual.} = @@ -2177,13 +2236,13 @@ The return type can be referred to as `-> '0`, but this is optional and often no #include class CppPrinter { public: - + virtual void printConst(char* message) const { std::cout << "Const Message: " << message << std::endl; } virtual void printConstRef(char* message, const int& flag) const { std::cout << "Const Ref Message: " << message << std::endl; - } + } }; """.} @@ -2194,7 +2253,7 @@ type proc printConst(self: CppPrinter; message:cstring) {.importcpp.} CppPrinter().printConst(message) -# override is optional. +# override is optional. proc printConst(self: NimPrinter; message: cstring) {.virtual: "$1('2 #2) const override".} = echo "NimPrinter: " & $message @@ -2224,10 +2283,10 @@ proc makeFoo(x: int32): Foo {.constructor.} = ``` -It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. +It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. Notice, inside the body of the constructor one has access to `this` which is of the type `ptr Foo`. No `result` variable is available. -Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints. +Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints. For example: @@ -2242,11 +2301,11 @@ struct CppClass { this->x = inX; this->y = inY; } - //CppClass() = default; + //CppClass() = default; }; """.} -type +type CppClass* {.importcpp, inheritable.} = object x: int32 y: int32 @@ -2256,11 +2315,11 @@ proc makeNimClass(x: int32): NimClass {.constructor:"NimClass('1 #1) : CppClass( this.x = x # Optional: define the default constructor explicitly -proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = +proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = this.x = 1 ``` -In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. +In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized. diff --git a/doc/nimc.md b/doc/nimc.md index 7c42c7c1b754..9c6ea70330e0 100644 --- a/doc/nimc.md +++ b/doc/nimc.md @@ -481,9 +481,13 @@ They are: 5. nl_types. No headers for this. 6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used. + DLL generation ============== +**Note**: The same rules apply to `lib*.so` shared object files on UNIX. For better +readability only the DLL version is decribed here. + Nim supports the generation of DLLs. However, there must be only one instance of the GC per process/address space. This instance is contained in ``nimrtl.dll``. This means that every generated Nim DLL depends From 808c9c6c2a93e6076c17b6f9bbab367df4c27772 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sun, 23 Jul 2023 15:35:30 +0200 Subject: [PATCH 077/347] Testcase for #22008 (#22320) Testcase Co-authored-by: SirOlaf <> --- tests/exception/t22008.nim | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/exception/t22008.nim diff --git a/tests/exception/t22008.nim b/tests/exception/t22008.nim new file mode 100644 index 000000000000..c0758e7b45bf --- /dev/null +++ b/tests/exception/t22008.nim @@ -0,0 +1,8 @@ +template detect(v: untyped) = + doAssert typeof(v) is int + +detect: + try: + raise (ref ValueError)() + except ValueError: + 42 \ No newline at end of file From 49a108b3027914eec18fab4bc47d9b4846eb362e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Sun, 23 Jul 2023 15:42:20 +0100 Subject: [PATCH 078/347] Expands codegenDecl to work in function params. fixes #22306 (#22307) * Expands codegenDecl to work in function params. fixes #22306 * makes the test more concrete so T{lit} params dont match * adds sfCodegenDecl --- compiler/ast.nim | 1 + compiler/ccgtypes.nim | 34 +++++++++++++++++++++------------- compiler/cgen.nim | 4 ++-- compiler/pragmas.nim | 3 ++- compiler/semtypes.nim | 4 +++- compiler/sigmatch.nim | 2 +- tests/cpp/tcodegendecl.nim | 17 +++++++++++++++++ 7 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 tests/cpp/tcodegendecl.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 0be26039149b..706c0d38fb9b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -314,6 +314,7 @@ type # an infinite loop, this flag is used as a sentinel to stop it. sfVirtual # proc is a C++ virtual function sfByCopy # param is marked as pass bycopy + sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl TSymFlags* = set[TSymFlag] diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 307d1f2e0ef8..e4a0fe84bcde 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -483,6 +483,9 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr res.add(substr(frmt, start, i - 1)) frmt = res +template cgDeclFrmt*(s: PSym): string = + s.constraint.strVal + proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var string, check: var IntSet, declareEnvironment=true; weakDep=false;) = @@ -535,7 +538,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var name = param.loc.r types.add typ names.add name - args.add types[^1] & " " & names[^1] + if sfCodegenDecl notin param.flags: + args.add types[^1] & " " & names[^1] + else: + args.add runtimeFormat(param.cgDeclFrmt, [types[^1], names[^1]]) multiFormat(params, @['\'', '#'], [types, names]) multiFormat(superCall, @['\'', '#'], [types, names]) @@ -570,19 +576,24 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, fillParamName(m, param) fillLoc(param.loc, locParam, t.n[i], param.paramStorageLoc) + var typ: Rope if ccgIntroducedPtr(m.config, param, t[0]) and descKind == dkParam: - params.add(getTypeDescWeak(m, param.typ, check, descKind)) - params.add("*") + typ = (getTypeDescWeak(m, param.typ, check, descKind)) + typ.add("*") incl(param.loc.flags, lfIndirect) param.loc.storage = OnUnknown elif weakDep: - params.add(getTypeDescWeak(m, param.typ, check, descKind)) + typ = (getTypeDescWeak(m, param.typ, check, descKind)) else: - params.add(getTypeDescAux(m, param.typ, check, descKind)) - params.add(" ") + typ = (getTypeDescAux(m, param.typ, check, descKind)) + typ.add(" ") if sfNoalias in param.flags: - params.add("NIM_NOALIAS ") - params.add(param.loc.r) + typ.add("NIM_NOALIAS ") + if sfCodegenDecl notin param.flags: + params.add(typ) + params.add(param.loc.r) + else: + params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.r]) # declare the len field for open arrays: var arr = param.typ.skipTypes({tyGenericInst}) if arr.kind in {tyVar, tyLent, tySink}: arr = arr.lastSon @@ -721,9 +732,6 @@ proc fillObjectFields*(m: BModule; typ: PType) = discard getRecordFields(m, typ, check) proc mangleDynLibProc(sym: PSym): Rope - -template cgDeclFrmt*(s: PSym): string = - s.constraint.strVal proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, check: var IntSet, hasField:var bool): Rope = @@ -770,7 +778,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope, var baseType: string if typ[0] != nil: baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField) - if typ.sym == nil or typ.sym.constraint == nil: + if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: result = structOrUnion & " " & name result.add(getRecordDescAux(m, typ, name, baseType, check, hasField)) let desc = getRecordFields(m, typ, check) @@ -1198,7 +1206,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) name.add("_actual") # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! - if prc.constraint.isNil: + if sfCodegenDecl notin prc.flags: if lfExportLib in prc.loc.flags: if isHeaderFile in m.flags: result.add "N_LIB_IMPORT " diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ed149ed0e515..0242ae2f70b7 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -590,7 +590,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope = genCLineDir(result, p, n.info, p.config) result.add getTypeDesc(p.module, s.typ, dkVar) - if s.constraint.isNil: + if sfCodegenDecl notin s.flags: if sfRegister in s.flags: result.add(" register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # decl.add(" GC_GUARD") @@ -621,7 +621,7 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = let s = n.sym - if s.constraint.isNil: + if sfCodegenDecl notin s.flags: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] if p.hcrOn: decl.add("static ") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 0d95f596c519..258836ca3812 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -84,7 +84,7 @@ const wGensym, wInject, wIntDefine, wStrDefine, wBoolDefine, wDefine, wCompilerProc, wCore} - paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy} + paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, @@ -253,6 +253,7 @@ proc processVirtual(c: PContext, n: PNode, s: PSym) = proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = sym.constraint = getStrLitNode(c, n) + sym.flags.incl sfCodegenDecl proc processMagic(c: PContext, n: PNode, s: PSym) = #if sfSystemModule notin c.module.flags: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index aa5f0a79b47c..60550de570ac 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1356,7 +1356,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen) arg.typ = finalType arg.position = counter - arg.constraint = constraint + if constraint != nil: + #only replace the constraint when it has been set as arg could contain codegenDecl + arg.constraint = constraint inc(counter) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e90f1524b6b9..4aa51977ab65 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2432,7 +2432,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int return template checkConstraint(n: untyped) {.dirty.} = - if not formal.constraint.isNil: + if not formal.constraint.isNil and sfCodegenDecl notin formal.flags: if matchNodeKinds(formal.constraint, n): # better match over other routines with no such restriction: inc(m.genericMatches, 100) diff --git a/tests/cpp/tcodegendecl.nim b/tests/cpp/tcodegendecl.nim new file mode 100644 index 000000000000..e128c5eb7d93 --- /dev/null +++ b/tests/cpp/tcodegendecl.nim @@ -0,0 +1,17 @@ +discard """ + targets: "cpp" + cmd: "nim cpp $file" + output: "3" +""" + +{.emit:"""/*TYPESECTION*/ + int operate(int x, int y, int (*func)(const int&, const int&)){ + return func(x, y); + }; +""".} + +proc operate(x, y: int32, fn: proc(x, y: int32 ): int32 {.cdecl.}): int32 {.importcpp:"$1(@)".} + +proc add(a {.codegenDecl:"const $#& $#".}, b {.codegenDecl:"const $# $#", byref.}: int32): int32 {.cdecl.} = a + b + +echo operate(1, 2, add) \ No newline at end of file From 8216d7dd4635db3c0566155c35bb6f339daedbe3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 24 Jul 2023 23:22:50 +0800 Subject: [PATCH 079/347] fixes #22321; fixes building DLL with --noMain still produces a DllMain (#22323) * fixes #22321; Building DLL with --noMain produces an unexpected DllMain on devel branch * remove implicit nomain --- compiler/cgen.nim | 2 +- compiler/commands.nim | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0242ae2f70b7..363bbce42e95 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1636,7 +1636,7 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) - if optNoMain notin m.config.globalOptions or optGenDynLib in m.config.globalOptions: + if optNoMain notin m.config.globalOptions: if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(m.s[cfsProcs]) m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L" diff --git a/compiler/commands.nim b/compiler/commands.nim index 5396cbe0d37d..f14c3d1d100e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -802,7 +802,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "consoleapp") of "lib": incl(conf.globalOptions, optGenDynLib) - incl(conf.globalOptions, optNoMain) excl(conf.globalOptions, optGenGuiApp) defineSymbol(conf.symbols, "library") defineSymbol(conf.symbols, "dll") From dce714b2598c41e36113a4339fb9fb14655bc090 Mon Sep 17 00:00:00 2001 From: Khaled Hammouda Date: Mon, 24 Jul 2023 13:48:41 -0400 Subject: [PATCH 080/347] Fix grammar top rule (#22325) change stmt to complexOrSimpleStmt in the top grammar rule --- compiler/parser.nim | 2 +- doc/grammar.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 1b8fd70a60ac..7d12c2a7859c 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -310,7 +310,7 @@ proc checkBinary(p: Parser) {.inline.} = if p.tok.spacing == {tsTrailing}: parMessage(p, warnInconsistentSpacing, prettyTok(p.tok)) -#| module = stmt ^* (';' / IND{=}) +#| module = complexOrSimpleStmt ^* (';' / IND{=}) #| #| comma = ',' COMMENT? #| semicolon = ';' COMMENT? diff --git a/doc/grammar.txt b/doc/grammar.txt index 458eeb54a67a..3096eecb52b0 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -1,5 +1,5 @@ # This file is generated by compiler/parser.nim. -module = stmt ^* (';' / IND{=}) +module = complexOrSimpleStmt ^* (';' / IND{=}) comma = ',' COMMENT? semicolon = ';' COMMENT? colon = ':' COMMENT? From 1c2ccfad08191e936fadd52450b53dfea105a34d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 25 Jul 2023 18:08:32 +0800 Subject: [PATCH 081/347] fixes #22301; fixes #22324; rejects branch initialization with a runtime discriminator with defaults (#22303) * fixes #22301; rejects branch initialization with a runtime discriminator with defaults * undefault nimPreviewRangeDefault * fixes tests * use oldCheckDefault --- compiler/sem.nim | 34 +++++++++++++++++---------------- compiler/semmagic.nim | 4 ++-- compiler/semobjconstr.nim | 13 ++++++++++++- config/nim.cfg | 1 - tests/objects/t22301.nim | 17 +++++++++++++++++ tests/system/tfielditerator.nim | 16 +++++++++++++++- 6 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 tests/objects/t22301.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index f92853d9e344..3324da55c3a0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -553,17 +553,17 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int = if endsWithElse: return caseExpr.len - 1 -proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] -proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode -proc defaultNodeField(c: PContext, a: PNode): PNode +proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] +proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode +proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink} -proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] = +proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] = case recNode.kind of nkRecList: for field in recNode: - result.add defaultFieldsForTuple(c, field, hasDefault) + result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault) of nkSym: let field = recNode.sym let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes) @@ -572,7 +572,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s result.add newTree(nkExprColonExpr, recNode, field.ast) else: if recType.kind in {tyObject, tyArray, tyTuple}: - let asgnExpr = defaultNodeField(c, recNode, recNode.typ) + let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault) if asgnExpr != nil: hasDefault = true asgnExpr.flags.incl nfSkipFieldChecking @@ -591,11 +591,11 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s else: doAssert false -proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] = +proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] = case recNode.kind of nkRecList: for field in recNode: - result.add defaultFieldsForTheUninitialized(c, field) + result.add defaultFieldsForTheUninitialized(c, field, checkDefault) of nkRecCase: let discriminator = recNode[0] var selectedBranch: int @@ -604,19 +604,21 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] = # None of the branches were explicitly selected by the user and no value # was given to the discrimator. We can assume that it will be initialized # to zero and this will select a particular branch as a result: + if checkDefault: # don't add defaults when checking whether a case branch has default fields + return defaultValue = newIntNode(nkIntLit#[c.graph]#, 0) defaultValue.typ = discriminator.typ selectedBranch = recNode.pickCaseBranchIndex defaultValue defaultValue.flags.incl nfSkipFieldChecking result.add newTree(nkExprColonExpr, discriminator, defaultValue) - result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1]) + result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault) of nkSym: let field = recNode.sym let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes) if field.ast != nil: #Try to use default value result.add newTree(nkExprColonExpr, recNode, field.ast) elif recType.kind in {tyObject, tyArray, tyTuple}: - let asgnExpr = defaultNodeField(c, recNode, recNode.typ) + let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault) if asgnExpr != nil: asgnExpr.typ = recNode.typ asgnExpr.flags.incl nfSkipFieldChecking @@ -624,17 +626,17 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] = else: doAssert false -proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode = +proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode = let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes) if aTypSkip.kind == tyObject: - let child = defaultFieldsForTheUninitialized(c, aTypSkip.n) + let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault) if child.len > 0: var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp)) asgnExpr.typ = aTyp asgnExpr.sons.add child result = semExpr(c, asgnExpr) elif aTypSkip.kind == tyArray: - let child = defaultNodeField(c, a, aTypSkip[1]) + let child = defaultNodeField(c, a, aTypSkip[1], checkDefault) if child != nil: let node = newNode(nkIntLit) @@ -647,15 +649,15 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode = elif aTypSkip.kind == tyTuple: var hasDefault = false if aTypSkip.n != nil: - let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault) + let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault) if hasDefault and children.len > 0: result = newNodeI(nkTupleConstr, a.info) result.typ = aTyp result.sons.add children result = semExpr(c, result) -proc defaultNodeField(c: PContext, a: PNode): PNode = - result = defaultNodeField(c, a, a.typ) +proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode = + result = defaultNodeField(c, a, a.typ, checkDefault) include semtempl, semgnrc, semstmts, semexprs diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index f94d8dc33ebb..ad7e9821b78a 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -21,7 +21,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode = asgnExpr.typ = typ var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0] while true: - asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n) + asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false) let base = t[0] if base == nil: break @@ -647,7 +647,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mDefault: result = checkDefault(c, n) let typ = result[^1].typ.skipTypes({tyTypeDesc}) - let defaultExpr = defaultNodeField(c, result[^1], typ) + let defaultExpr = defaultNodeField(c, result[^1], typ, false) if defaultExpr != nil: result = defaultExpr of mZeroDefault: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index d4eba2112cd6..9b17676ee7f2 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -21,6 +21,7 @@ type # set this to true while visiting # parent types. missingFields: seq[PSym] # Fields that the user failed to specify + checkDefault: bool # Checking defaults InitStatus = enum # This indicates the result of object construction initUnknown @@ -342,6 +343,16 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, # All bets are off. If any of the branches has a mandatory # fields we must produce an error: for i in 1.. 0: + localError(c.config, discriminatorVal.info, "branch initialization " & + "with a runtime discriminator is not supported " & + "for a branch whose fields have default values.") discard collectMissingCaseFields(c, n[i], constrCtx, @[]) of nkSym: let field = n.sym @@ -353,7 +364,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, result.defaults.add newTree(nkExprColonExpr, n, field.ast) else: if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass - let defaultExpr = defaultNodeField(c, n) + let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault) if defaultExpr != nil: result.status = initUnknown result.defaults.add newTree(nkExprColonExpr, n, defaultExpr) diff --git a/config/nim.cfg b/config/nim.cfg index efb0581218d7..1470de78056a 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -21,7 +21,6 @@ cc = gcc #hint[XDeclaredButNotUsed]=off threads:on -define:nimPreviewRangeDefault # Examples of how to setup a cross-compiler: # Nim can target architectures and OSes different than the local host diff --git a/tests/objects/t22301.nim b/tests/objects/t22301.nim new file mode 100644 index 000000000000..8746bf584dec --- /dev/null +++ b/tests/objects/t22301.nim @@ -0,0 +1,17 @@ +discard """ + errormsg: "branch initialization with a runtime discriminator is not supported for a branch whose fields have default values." +""" + +# bug #22301 +type + Enum = enum A, B + Object = object + case a: Enum + of A: + integer: int = 200 + of B: + time: string + +let x = A +let s = Object(a: x) +echo s \ No newline at end of file diff --git a/tests/system/tfielditerator.nim b/tests/system/tfielditerator.nim index d1fbf02f95f6..7e063c6cf82b 100644 --- a/tests/system/tfielditerator.nim +++ b/tests/system/tfielditerator.nim @@ -109,4 +109,18 @@ block titerator2: echo key, ": ", val for val in fields(co): - echo val \ No newline at end of file + echo val + +block: + type + Enum = enum A, B + Object = object + case a: Enum + of A: + integer: int + of B: + time: string + + let x = A + let s = Object(a: x) + doAssert s.integer == 0 From c0994c2dbdaaa6276b91c206d3377d68789f49ec Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 25 Jul 2023 17:56:14 +0200 Subject: [PATCH 082/347] [JS] Fix casting to ints (#22327) * [JS] Fix casting to ints * Simplify `genCast` by using `asUintN`/`asIntN` --- compiler/jsgen.nim | 26 +++++++------------------- tests/cast/tcast.nim | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 tests/cast/tcast.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 1fbf6c74c70e..4a62cbf9ebf6 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2727,26 +2727,14 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = let fromInt = (src.kind in tyInt..tyInt32) let fromUint = (src.kind in tyUInt..tyUInt32) - if toUint and (fromInt or fromUint): - let trimmer = unsignedTrimmer(dest.size) - r.res = "($1 $2)" % [r.res, trimmer] - elif toUint and src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: - r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res] + if toUint: + if fromInt or fromUint: + r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res] + elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res] elif toInt: - if fromInt: - return - elif fromUint: - if src.size == 4 and dest.size == 4: - # XXX prevent multi evaluations - r.res = "($1 | 0)" % [r.res] - else: - let trimmer = unsignedTrimmer(dest.size) - let minuend = case dest.size - of 1: "0xfe" - of 2: "0xfffe" - of 4: "0xfffffffe" - else: "" - r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] + if fromInt or fromUint: + r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res] elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res] elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: diff --git a/tests/cast/tcast.nim b/tests/cast/tcast.nim new file mode 100644 index 000000000000..205444ea3a6c --- /dev/null +++ b/tests/cast/tcast.nim @@ -0,0 +1,21 @@ +discard """ + targets: "c cpp js" +""" + +proc main() = + block: # bug #16806 + let + a = 42u16 + b = cast[int16](a) + doAssert a.int16 == 42 + doAssert b in int16.low..int16.high + + block: # bug #16808 + doAssert cast[int8](cast[uint8](int8(-12))) == int8(-12) + doAssert cast[int16](cast[uint16](int16(-12))) == int16(-12) + doAssert cast[int32](cast[uint32](int32(-12))) == int32(-12) + + doAssert cast[int8](int16.high) == -1 + +static: main() +main() From 11c8dfc9b3199a12e5aadadd1491f63894b489ec Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 26 Jul 2023 10:04:34 +0800 Subject: [PATCH 083/347] fixes docs (#22331) --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 858571d61b3d..fc8476b40db4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -914,7 +914,7 @@ proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} = ## See also: ## * `zeroDefault <#zeroDefault,typedesc[T]>`_ ## - runnableExamples: + runnableExamples("-d:nimPreviewRangeDefault"): assert (int, float).default == (0, 0.0) type Foo = object a: range[2..6] From db77c984714aeafdb61aba092f54fd22a482deed Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 27 Jul 2023 23:06:30 +0200 Subject: [PATCH 084/347] [JS] Fix bitwise ops & shifts (#22340) * [JS] Fix bitwise ops & shifts * Test `int64` & `uint64` only with `jsbigint64` --- compiler/jsgen.nim | 83 +++++++++++++++++++++++++++++---------------- tests/int/tints.nim | 49 +++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 31 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 4a62cbf9ebf6..ce1fdb1a5e22 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -556,16 +556,16 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string, r.res = frmt % [a, b, tmp, tmp2] r.kind = resExpr -proc unsignedTrimmerJS(size: BiggestInt): Rope = +proc unsignedTrimmer(size: BiggestInt): string = case size - of 1: rope"& 0xff" - of 2: rope"& 0xffff" - of 4: rope">>> 0" - else: rope"" - + of 1: "& 0xff" + of 2: "& 0xffff" + of 4: ">>> 0" + else: "" -template unsignedTrimmer(size: BiggestInt): Rope = - size.unsignedTrimmerJS +proc signedTrimmer(size: BiggestInt): string = + # sign extension is done by shifting to the left and then back to the right + "<< $1 >> $1" % [$(32 - size * 8)] proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: static[bool] = false) = @@ -626,6 +626,13 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = template applyFormat(frmtA, frmtB) = if i == 0: applyFormat(frmtA) else: applyFormat(frmtB) + template bitwiseExpr(op: string) = + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind in {tyUInt, tyUInt32}: + r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc] + else: + r.res = "($1 $2 $3)" % [xLoc, op, yLoc] + case op of mAddI: if i == 0: @@ -672,7 +679,19 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mSubF64: applyFormat("($1 - $2)", "($1 - $2)") of mMulF64: applyFormat("($1 * $2)", "($1 * $2)") of mDivF64: applyFormat("($1 / $2)", "($1 / $2)") - of mShrI: applyFormat("", "") + of mShrI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))") + elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("($1 >> BigInt($2))") + else: + if typ.kind in {tyInt..tyInt32}: + let trimmerU = unsignedTrimmer(typ.size) + let trimmerS = signedTrimmer(typ.size) + r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS] + else: + applyFormat("($1 >>> $2)") of mShlI: let typ = n[1].typ.skipTypes(abstractVarRange) if typ.size == 8: @@ -683,21 +702,27 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = else: applyFormat("($1 * Math.pow(2, $2))") else: - applyFormat("($1 << $2)", "($1 << $2)") + if typ.kind in {tyUInt..tyUInt32}: + let trimmer = unsignedTrimmer(typ.size) + r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer] + else: + let trimmer = signedTrimmer(typ.size) + r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer] of mAshrI: let typ = n[1].typ.skipTypes(abstractVarRange) if typ.size == 8: - if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: - applyFormat("BigInt.asIntN(64, $1 >> BigInt($2))") - elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - applyFormat("BigInt.asUintN(64, $1 >> BigInt($2))") + if optJsBigInt64 in p.config.globalOptions: + applyFormat("($1 >> BigInt($2))") else: applyFormat("Math.floor($1 / Math.pow(2, $2))") else: - applyFormat("($1 >> $2)", "($1 >> $2)") - of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") - of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") - of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") + if typ.kind in {tyUInt..tyUInt32}: + applyFormat("($1 >>> $2)") + else: + applyFormat("($1 >> $2)") + of mBitandI: bitwiseExpr("&") + of mBitorI: bitwiseExpr("|") + of mBitxorI: bitwiseExpr("^") of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)") of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)") of mAddU: applyFormat("", "") @@ -733,7 +758,16 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mAbsI: applyFormat("absInt($1)", "Math.abs($1)") of mNot: applyFormat("!($1)", "!($1)") of mUnaryPlusI: applyFormat("+($1)", "+($1)") - of mBitnotI: applyFormat("~($1)", "~($1)") + of mBitnotI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind in {tyUInt..tyUInt64}: + if typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, ~($1))") + else: + let trimmer = unsignedTrimmer(typ.size) + r.res = "(~($1) $2)" % [xLoc, trimmer] + else: + applyFormat("~($1)") of mUnaryPlusF64: applyFormat("+($1)", "+($1)") of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") @@ -760,17 +794,6 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = arithAux(p, n, r, op) of mModI: arithAux(p, n, r, op) - of mShrI: - var x, y: TCompRes - gen(p, n[1], x) - gen(p, n[2], y) - let typ = n[1].typ.skipTypes(abstractVarRange) - if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: - r.res = "BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))" % [x.rdLoc, y.rdLoc] - elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - r.res = "($1 >> BigInt($2))" % [x.rdLoc, y.rdLoc] - else: - r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc] of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) of mEqRef: diff --git a/tests/int/tints.nim b/tests/int/tints.nim index cb77d4d8969e..a7d27d736976 100644 --- a/tests/int/tints.nim +++ b/tests/int/tints.nim @@ -92,6 +92,53 @@ block: # Casts to uint # issue #7174 let c = 1'u let val = c > 0 -doAssert val +doAssert val + +block: # bug #6752 + when not defined(js) or (defined(js) and compileOption("jsbigint64")): + let x = 711127'i64 + doAssert x * 86400'i64 == 61441372800'i64 + +block: # bug #17604 + let a = 2147483648'u + doAssert (a and a) == a + doAssert (a or 0) == a + +block: # bitwise not + let + z8 = 0'u8 + z16 = 0'u16 + z32 = 0'u32 + z64 = 0'u64 + doAssert (not z8) == uint8.high + doAssert (not z16) == uint16.high + doAssert (not z32) == uint32.high + when not defined(js) or (defined(js) and compileOption("jsbigint64")): + doAssert (not z64) == uint64.high + +block: # shl + let i8 = int8.high + let i16 = int16.high + let i32 = int32.high + let i64 = int64.high + doAssert i8 shl 1 == -2 + doAssert i8 shl 2 == -4 + doAssert i16 shl 1 == -2 + doAssert i16 shl 2 == -4 + doAssert i32 shl 1 == -2 + doAssert i32 shl 2 == -4 + when not defined(js) or (defined(js) and compileOption("jsbigint64")): + doAssert i64 shl 1 == -2 + doAssert i64 shl 2 == -4 + + let u8 = uint8.high + let u16 = uint16.high + let u32 = uint32.high + let u64 = uint64.high + doAssert u8 shl 1 == u8 - 1 + doAssert u16 shl 1 == u16 - 1 + doAssert u32 shl 1 == u32 - 1 + when not defined(js) or (defined(js) and compileOption("jsbigint64")): + doAssert u64 shl 1 == u64 - 1 echo("Success") #OUT Success From f1ac979184ad7fa0d8c44415e781181a00a0095f Mon Sep 17 00:00:00 2001 From: "Eric N. Vander Weele" Date: Thu, 27 Jul 2023 17:07:03 -0400 Subject: [PATCH 085/347] Remove declared and not used variable in packedsets.bitincl (#22334) When compiling code that uses PackedSet with warnings enabled, `var ret` in `bitincl` emits a "XDeclaredButNotUsed" warning. --- lib/std/packedsets.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index e8e03af9e383..04fa78ada9a2 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -109,7 +109,6 @@ proc intSetPut[A](t: var PackedSet[A], key: int): Trunk = t.data[h] = result proc bitincl[A](s: var PackedSet[A], key: int) {.inline.} = - var ret: Trunk var t = intSetPut(s, key shr TrunkShift) var u = key and TrunkMask t.bits[u shr IntShift] = t.bits[u shr IntShift] or From f0f3904ff04a46bae6f876b0326162354466f415 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 29 Jul 2023 16:57:03 +0800 Subject: [PATCH 086/347] implement `ensureMove` (#22339) * implement `ensureMove` * use an additional flag * improve some logics * progress: fixes discard ensureMove * forbids nested expressions * improve error messages * checkpoint * fixes cursor * ADD MORE TESTS * fixes cursorinference again * tiny cleanup * improve error messages * fixes docs * implement comments add more tests * fixes js --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 2 + compiler/condsyms.nim | 1 + compiler/injectdestructors.nim | 41 ++++++++++- compiler/jsgen.nim | 2 + compiler/lineinfos.nim | 2 + compiler/semmagic.nim | 4 + compiler/varpartitions.nim | 4 + compiler/vmgen.nim | 2 + lib/system.nim | 10 +++ tests/system/tensuremove.nim | 130 +++++++++++++++++++++++++++++++++ tests/system/tensuremove1.nim | 16 ++++ tests/system/tensuremove2.nim | 15 ++++ tests/system/tensuremove3.nim | 28 +++++++ 14 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 tests/system/tensuremove.nim create mode 100644 tests/system/tensuremove1.nim create mode 100644 tests/system/tensuremove2.nim create mode 100644 tests/system/tensuremove3.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 706c0d38fb9b..539b6e954760 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -690,7 +690,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, mDup, mDestroy, mTrace, + mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace, mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2b9f4221f0ab..712b874d93d8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2622,6 +2622,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAccessTypeField: genAccessTypeField(p, e, d) of mSlice: genSlice(p, e, d) of mTrace: discard "no code to generate" + of mEnsureMove: + expr(p, e[1], d) else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index c680504494ca..1146bed146e0 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -157,3 +157,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasSendable") defineSymbol("nimAllowNonVarDestructor") defineSymbol("nimHasQuirky") + defineSymbol("nimHasEnsureMove") diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d7f4e38d2c71..590012806c4f 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -36,6 +36,7 @@ type body: PNode otherUsage: TLineInfo inUncheckedAssignSection: int + inEnsureMove: int Scope = object # we do scope-based memory management. # a scope is comparable to an nkStmtListExpr like @@ -332,6 +333,9 @@ proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode = assert ri.typ != nil proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode = + if c.inEnsureMove > 0: + localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri & + "', which introduces an implicit copy") let t = dest.typ if tfHasOwned in t.flags and ri.kind != nkNilLit: # try to improve the error message here: @@ -400,7 +404,7 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) - if not hasDestructor(c, n.typ): + if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0: assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) result = copyTree(n) else: @@ -419,7 +423,8 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = result.add v let nn = skipConv(n) - c.genMarkCyclic(result, nn) + if hasDestructor(c, n.typ): + c.genMarkCyclic(result, nn) let wasMovedCall = c.genWasMoved(nn) result.add wasMovedCall result.add tempAsNode @@ -462,6 +467,9 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & "if possible, rearrange your program's control flow to prevent it") % $n) + if c.inEnsureMove > 0: + localError(c.graph.config, n.info, errFailedMove, + ("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: assert(not containsManagedMemory(n.typ)) @@ -765,7 +773,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing result = passCopyToSink(n, c, s) elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} + nkCallKinds + nkLiterals: - result = p(n, c, s, consumed) + if n.kind in nkCallKinds and n[0].kind == nkSym: + if n[0].sym.magic == mEnsureMove: + inc c.inEnsureMove + result = p(n[1], c, s, sinkArg) + dec c.inEnsureMove + else: + result = p(n, c, s, consumed) + else: + result = p(n, c, s, consumed) elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)): # Sinked params can be consumed only once. We need to reset the memory @@ -837,6 +853,12 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if mode == normal and isRefConstr: result = ensureDestruction(result, n, c, s) of nkCallKinds: + if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove: + inc c.inEnsureMove + result = p(n[1], c, s, sinkArg) + dec c.inEnsureMove + return + let inSpawn = c.inSpawn if n[0].kind == nkSym and n[0].sym.magic == mSpawn: c.inSpawn.inc @@ -1069,6 +1091,11 @@ proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess)) proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode = + var ri = ri + var isEnsureMove = 0 + if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mEnsureMove: + ri = ri[1] + isEnsureMove = 1 if sameLocation(dest, ri): # rule (self-assignment-removal): result = newNodeI(nkEmpty, dest.info) @@ -1103,13 +1130,17 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy else: result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags) else: + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) of nkBracket: # array constructor if ri.len > 0 and isDangerousSeq(ri.typ): + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) else: @@ -1127,7 +1158,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: @@ -1145,7 +1178,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ce1fdb1a5e22..8be4d9d07569 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2405,6 +2405,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = genMove(p, n, r) of mDup: genDup(p, n, r) + of mEnsureMove: + gen(p, n[1], r) else: genCall(p, n, r) #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 37adc5660ee2..785b10197244 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -44,6 +44,7 @@ type errRstSandboxedDirective, errProveInit, # deadcode errGenerated, + errFailedMove, errUser, # warnings warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", @@ -128,6 +129,7 @@ const errRstSandboxedDirective: "disabled directive: '$1'", errProveInit: "Cannot prove that '$1' is initialized.", # deadcode errGenerated: "$1", + errFailedMove: "$1", errUser: "$1", warnCannotOpenFile: "cannot open '$1'", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index ad7e9821b78a..97a32077448c 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -666,5 +666,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = n if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and expectedType.kind == tySequence and result.typ[0].kind == tyEmpty: result.typ = expectedType # type inference for empty sequence # bug #21377 + of mEnsureMove: + result = n + if isAssignable(c, n[1]) notin {arLValue, arLocalLValue}: + localError(c.config, n.info, "'" & $n[1] & "'" & " is not a mutable location; it cannot be moved") else: result = n diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 6598ef508b92..6290b311fb2a 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -707,6 +707,10 @@ proc traverse(c: var Partitions; n: PNode) = let L = if parameters != nil: parameters.len else: 0 let m = getMagic(n) + if m == mEnsureMove and n[1].kind == nkSym: + # we know that it must be moved so it cannot be a cursor + noCursor(c, n[1].sym) + for i in 1.. Date: Sat, 29 Jul 2023 17:05:31 +0100 Subject: [PATCH 087/347] fixes an issue where byref wasnt properly handled when using it in a generic param (#22337) * fixes an issue where byref wasnt properly handled when using it in a generic param * removes unreachable check --- compiler/ccgtypes.nim | 13 ++++++++++--- tests/cpp/tpassbypragmas.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/cpp/tpassbypragmas.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e4a0fe84bcde..2aa92c130e77 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -18,6 +18,7 @@ type TypeDescKind = enum dkParam #skParam dkRefParam #param passed by ref when {.byref.} is used. Cpp only. C goes straight to dkParam and is handled as a regular pointer + dkRefGenericParam #param passed by ref when {.byref.} is used that is also a generic. Cpp only. C goes straight to dkParam and is handled as a regular pointer dkVar #skVar dkField #skField dkResult #skResult @@ -519,7 +520,10 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var var param = t.n[i].sym var descKind = dkParam if optByRef in param.options: - descKind = dkRefParam + if param.typ.kind == tyGenericInst: + descKind = dkRefGenericParam + else: + descKind = dkRefParam var typ, name : string fillParamName(m, param) fillLoc(param.loc, locParam, t.n[i], @@ -570,7 +574,10 @@ proc genProcParams(m: BModule; t: PType, rettype, params: var Rope, var param = t.n[i].sym var descKind = dkParam if m.config.backend == backendCpp and optByRef in param.options: - descKind = dkRefParam + if param.typ.kind == tyGenericInst: + descKind = dkRefGenericParam + else: + descKind = dkRefParam if isCompileTimeOnly(param.typ): continue if params != "(": params.add(", ") fillParamName(m, param) @@ -873,7 +880,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = getTypePre(m, t, sig) if result != "" and t.kind != tyOpenArray: excl(check, t.id) - if kind == dkRefParam: + if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst: result.add("&") return case t.kind diff --git a/tests/cpp/tpassbypragmas.nim b/tests/cpp/tpassbypragmas.nim new file mode 100644 index 000000000000..f4301656afb4 --- /dev/null +++ b/tests/cpp/tpassbypragmas.nim @@ -0,0 +1,27 @@ +discard """ + targets: "cpp" + cmd: "nim cpp $file" +""" +{.emit:"""/*TYPESECTION*/ + + template + struct Box { + T first; + }; + struct Foo { + void test(void (*func)(Box& another)){ + + }; + }; +""".} + +type + Foo {.importcpp.} = object + Box[T] {.importcpp:"Box<'0>".} = object + first: T + +proc test(self: Foo, fn: proc(another {.byref.}: Box[Foo]) {.cdecl.}) {.importcpp.} + +proc fn(another {.byref.} : Box[Foo]) {.cdecl.} = discard + +Foo().test(fn) \ No newline at end of file From 19d1fe7af3ac728327732f3d54f0a3333d0b3328 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 30 Jul 2023 02:21:22 -0300 Subject: [PATCH 088/347] Add Valgrind (#22346) * . * Add Valgrind for Bisect bot in GitHub Actions --- .github/workflows/bisects.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml index c755ef5a2721..a8200c1f9654 100644 --- a/.github/workflows/bisects.yml +++ b/.github/workflows/bisects.yml @@ -15,6 +15,9 @@ jobs: with: nim-version: 'devel' + - name: Install Dependencies + run: sudo apt-get install --no-install-recommends -yq valgrind + - uses: juancarlospaco/nimrun-action@nim with: github-token: ${{ secrets.GITHUB_TOKEN }} From 281016a8022b8e8308e3e578b2c2daa6df4a66a1 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 31 Jul 2023 02:43:52 +0800 Subject: [PATCH 089/347] add a changelog for `ensureMove` (#22347) * add a changelog for `ensureMove` * Update changelogs/changelog_2_0_0_details.md --------- Co-authored-by: Andreas Rumpf --- changelogs/changelog_2_0_0_details.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md index e3895639ad78..f6393b2db710 100644 --- a/changelogs/changelog_2_0_0_details.md +++ b/changelogs/changelog_2_0_0_details.md @@ -344,6 +344,7 @@ + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`. - Added `BackwardsIndex` overload for `CacheSeq`. - Added support for nested `with` blocks in `std/with`. +- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time. [//]: # "Deprecations:" From d51bc084fd6277dfe2ebd6040f0dd6c281c83b6d Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 31 Jul 2023 16:58:59 +0800 Subject: [PATCH 090/347] remove thread duplicated code (#22348) --- lib/system/threadimpl.nim | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim index 94db23336528..285b8f5e7fac 100644 --- a/lib/system/threadimpl.nim +++ b/lib/system/threadimpl.nim @@ -20,13 +20,8 @@ when not defined(useNimRtl): threadType = ThreadType.NimThread when defined(gcDestructors): - proc allocThreadStorage(size: int): pointer = - result = c_malloc(csize_t size) - zeroMem(result, size) - proc deallocThreadStorage(p: pointer) = c_free(p) else: - template allocThreadStorage(size: untyped): untyped = allocShared0(size) template deallocThreadStorage(p: pointer) = deallocShared(p) template afterThreadRuns() = From 569ccc50ff9f4f48272a00c82556a495c0f721b9 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 31 Jul 2023 21:37:24 +0800 Subject: [PATCH 091/347] fixes #22174; fixes destructor examples (#22349) * fixes #22174; fixes destructor examples * Update doc/destructors.md Co-authored-by: Andreas Rumpf --------- Co-authored-by: Andreas Rumpf --- doc/destructors.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/destructors.md b/doc/destructors.md index 12def00537f0..3121335145bb 100644 --- a/doc/destructors.md +++ b/doc/destructors.md @@ -30,7 +30,7 @@ Motivating example With the language mechanisms described here, a custom seq could be written as: - ```nim + ```nim test type myseq*[T] = object len, cap: int @@ -570,7 +570,7 @@ that the pointer does not outlive its origin. No destructor call is injected for expressions of type `lent T` or of type `var T`. - ```nim + ```nim test type Tree = object kids: seq[Tree] @@ -584,7 +584,7 @@ for expressions of type `lent T` or of type `var T`. proc `[]`*(x: Tree; i: int): lent Tree = result = x.kids[i] # borrows from 'x', this is transformed into: - result = addr x.kids[i] + # result = addr x.kids[i] # This means 'lent' is like 'var T' a hidden pointer. # Unlike 'var' this hidden pointer cannot be used to mutate the object. @@ -715,7 +715,7 @@ The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be used to specialize the object traversal in order to avoid deep recursions: - ```nim + ```nim test type Node = ref object x, y: int32 left, right: Node @@ -730,8 +730,8 @@ used to specialize the object traversal in order to avoid deep recursions: let x = s.pop if x.left != nil: s.add(x.left) if x.right != nil: s.add(x.right) - # free the memory explicit: - dispose(x) + # free the memory explicitly: + `=dispose`(x) # notice how even the destructor for 's' is not called implicitly # anymore thanks to .nodestroy, so we have to call it on our own: `=destroy`(s) From b56df5c07f7dc9ac9d718ca47c10b0683a9b916f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:02:52 +0800 Subject: [PATCH 092/347] fixes #22246; generate `__builtin_unreachable` hints for case defaults (#22350) * fixes #22246; generate `__builtin_unreachable` hints * use elif * indentation * fixes holy enums in sim --- compiler/ccgstmts.nim | 7 +++++-- compiler/extccomp.nim | 5 +++-- testament/important_packages.nim | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 3197389814c3..448a437db728 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -971,8 +971,11 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = hasDefault = true exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n", []) - if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault: - lineF(p, cpsStmts, "default: __assume(0);$n", []) + if not hasDefault: + if hasBuiltinUnreachable in CC[p.config.cCompiler].props: + lineF(p, cpsStmts, "default: __builtin_unreachable();$n", []) + elif hasAssume in CC[p.config.cCompiler].props: + lineF(p, cpsStmts, "default: __assume(0);$n", []) lineF(p, cpsStmts, "}$n", []) if lend != "": fixLabel(p, lend) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 391f158f0ccf..3605d1dd274e 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -33,6 +33,7 @@ type hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax hasDeclspec, # CC has __declspec(X) hasAttribute, # CC has __attribute__((X)) + hasBuiltinUnreachable # CC has __builtin_unreachable TInfoCCProps* = set[TInfoCCProp] TInfoCC* = tuple[ name: string, # the short name of the compiler @@ -95,7 +96,7 @@ compiler gcc: produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasAttribute}) + hasAttribute, hasBuiltinUnreachable}) # GNU C and C++ Compiler compiler nintendoSwitchGCC: @@ -122,7 +123,7 @@ compiler nintendoSwitchGCC: produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasAttribute}) + hasAttribute, hasBuiltinUnreachable}) # LLVM Frontend for GCC/G++ compiler llvmGcc: diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 9016a0d3dc91..c632256efe06 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -146,7 +146,7 @@ pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" pkg "sdl1", "nim c -r src/sdl.nim" pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" pkg "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" -pkg "sim" +pkg "sim", url = "https://github.com/nim-lang/sim.nim" pkg "smtp", "nimble compileExample" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "ssostrings" From 0b3ddd4e47e12dda043a48ac24a8db823846d3da Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 31 Jul 2023 22:14:15 +0800 Subject: [PATCH 093/347] Revert "fixes #22246; generate `__builtin_unreachable` hints for case defaults" (#22351) Revert "fixes #22246; generate `__builtin_unreachable` hints for case defaults (#22350)" This reverts commit b56df5c07f7dc9ac9d718ca47c10b0683a9b916f. --- compiler/ccgstmts.nim | 7 ++----- compiler/extccomp.nim | 5 ++--- testament/important_packages.nim | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 448a437db728..3197389814c3 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -971,11 +971,8 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = hasDefault = true exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n", []) - if not hasDefault: - if hasBuiltinUnreachable in CC[p.config.cCompiler].props: - lineF(p, cpsStmts, "default: __builtin_unreachable();$n", []) - elif hasAssume in CC[p.config.cCompiler].props: - lineF(p, cpsStmts, "default: __assume(0);$n", []) + if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault: + lineF(p, cpsStmts, "default: __assume(0);$n", []) lineF(p, cpsStmts, "}$n", []) if lend != "": fixLabel(p, lend) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 3605d1dd274e..391f158f0ccf 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -33,7 +33,6 @@ type hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax hasDeclspec, # CC has __declspec(X) hasAttribute, # CC has __attribute__((X)) - hasBuiltinUnreachable # CC has __builtin_unreachable TInfoCCProps* = set[TInfoCCProp] TInfoCC* = tuple[ name: string, # the short name of the compiler @@ -96,7 +95,7 @@ compiler gcc: produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasAttribute, hasBuiltinUnreachable}) + hasAttribute}) # GNU C and C++ Compiler compiler nintendoSwitchGCC: @@ -123,7 +122,7 @@ compiler nintendoSwitchGCC: produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasAttribute, hasBuiltinUnreachable}) + hasAttribute}) # LLVM Frontend for GCC/G++ compiler llvmGcc: diff --git a/testament/important_packages.nim b/testament/important_packages.nim index c632256efe06..9016a0d3dc91 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -146,7 +146,7 @@ pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" pkg "sdl1", "nim c -r src/sdl.nim" pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" pkg "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" -pkg "sim", url = "https://github.com/nim-lang/sim.nim" +pkg "sim" pkg "smtp", "nimble compileExample" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "ssostrings" From 35ff70f36c0025018de3a8c5c993249d11b98292 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 31 Jul 2023 20:19:18 +0200 Subject: [PATCH 094/347] Tomorrow is the release. I hope. (#22353) --- changelogs/changelog_2_0_0.md | 2 +- changelogs/changelog_2_0_0_details.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index d954c774ef44..457cc62a6b9c 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -1,4 +1,4 @@ -# v2.0.0 - 2023-07-dd +# v2.0.0 - 2023-08-01 Version 2.0 is a big milestone with too many changes to list them all here. diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md index f6393b2db710..a38f2b40b701 100644 --- a/changelogs/changelog_2_0_0_details.md +++ b/changelogs/changelog_2_0_0_details.md @@ -1,4 +1,4 @@ -# v2.0.0 - yyyy-mm-dd +# v2.0.0 - 2023-08-01 ## Changes affecting backward compatibility From a23e53b4902d227353886d97ef50609709519dd9 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 1 Aug 2023 21:18:08 +0800 Subject: [PATCH 095/347] fixes #22262; fixes `-d:useMalloc` broken with `--mm:none` and `--threads on` (#22355) * fixes #22262; -d:useMalloc broken with --mm:none and threads on * fixes --- lib/system/mm/malloc.nim | 2 +- tests/system/tgcnone.nim | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index b24b6f1e0c3c..47f1a95ae48c 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -88,7 +88,7 @@ type proc alloc(r: var MemRegion, size: int): pointer = result = alloc(size) -proc alloc0Impl(r: var MemRegion, size: int): pointer = +proc alloc0(r: var MemRegion, size: int): pointer = result = alloc0Impl(size) proc dealloc(r: var MemRegion, p: pointer) = dealloc(p) proc deallocOsPages(r: var MemRegion) = discard diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim index 47c6c601451a..1ccb9e29c563 100644 --- a/tests/system/tgcnone.nim +++ b/tests/system/tgcnone.nim @@ -1,6 +1,7 @@ discard """ - matrix: "--gc:none -d:useMalloc --threads:off" + matrix: "--mm:none -d:useMalloc" """ # bug #15617 +# bug #22262 let x = 4 doAssert x == 4 From 1d2c27d2e67149240f97de48f721d95539c543e4 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 1 Aug 2023 22:48:52 +0800 Subject: [PATCH 096/347] bump the devel version to 211 (#22356) --- lib/system/compilation.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim index ba47c1f6924b..c36bd98319f2 100644 --- a/lib/system/compilation.nim +++ b/lib/system/compilation.nim @@ -1,16 +1,16 @@ const - NimMajor* {.intdefine.}: int = 1 + NimMajor* {.intdefine.}: int = 2 ## is the major number of Nim's version. Example: ## ``` ## when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard ## ``` # see also std/private/since - NimMinor* {.intdefine.}: int = 9 + NimMinor* {.intdefine.}: int = 1 ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 5 + NimPatch* {.intdefine.}: int = 1 ## is the patch number of Nim's version. ## Odd for devel, even for releases. From da368885da8850c0c87e6b9dcff64393985aff27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= Date: Tue, 1 Aug 2023 20:56:38 +0200 Subject: [PATCH 097/347] Fix the position of "Grey" in colors.nim (#22358) Update the position of "Grey" --- lib/pure/colors.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index b688fad54412..685b68b360e4 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -200,8 +200,8 @@ const colGoldenRod* = Color(0xDAA520) colGray* = Color(0x808080) colGreen* = Color(0x008000) - colGrey* = Color(0x808080) colGreenYellow* = Color(0xADFF2F) + colGrey* = Color(0x808080) colHoneyDew* = Color(0xF0FFF0) colHotPink* = Color(0xFF69B4) colIndianRed* = Color(0xCD5C5C) @@ -350,8 +350,8 @@ const "goldenrod": colGoldenRod, "gray": colGray, "green": colGreen, - "grey": colGrey, "greenyellow": colGreenYellow, + "grey": colGrey, "honeydew": colHoneyDew, "hotpink": colHotPink, "indianred": colIndianRed, From f3a7622514f24740c6b33f0c37ebe6339ad5b70d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:58:29 +0800 Subject: [PATCH 098/347] fixes #22360; compare with the half of randMax (#22361) * fixes #22360; compare with the half of randMax * add a test --- lib/pure/random.nim | 5 +---- tests/stdlib/trandom.nim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 422f42a8b805..88616594b14a 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -381,10 +381,7 @@ proc rand*[T: Ordinal](r: var Rand; t: typedesc[T]): T {.since: (1, 7, 1).} = when T is range or T is enum: result = rand(r, low(T)..high(T)) elif T is bool: - whenJsNoBigInt64: - result = (r.next or 0) < 0 - do: - result = cast[int64](r.next) < 0 + result = r.next < randMax div 2 else: whenJsNoBigInt64: result = cast[T](r.next shr (sizeof(uint)*8 - sizeof(T)*8)) diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index 8784b33ee419..920d429d4f8f 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -282,3 +282,21 @@ block: # bug #17898 for j in 0.. Date: Wed, 2 Aug 2023 17:00:34 +0800 Subject: [PATCH 099/347] fixes #22362; Compiler crashes with staticBoundsCheck on (#22363) --- compiler/cgendata.nim | 2 ++ compiler/jsgen.nim | 4 ++++ tests/pragmas/tpush.nim | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 4d15cf131b89..9cc146c4db5f 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -196,6 +196,8 @@ proc newProc*(prc: PSym, module: BModule): BProc = result = BProc( prc: prc, module: module, + optionsStack: if module.initProc != nil: module.initProc.optionsStack + else: @[], options: if prc != nil: prc.options else: module.config.options, blocks: @[initBlock()], diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8be4d9d07569..a5f4d29b2775 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -50,6 +50,7 @@ type graph: ModuleGraph config: ConfigRef sigConflicts: CountTable[SigHash] + initProc: PProc BModule = ref TJSGen TJSTypeKind = enum # necessary JS "types" @@ -158,6 +159,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, options: TOptions): PProc = result = PProc( blocks: @[], + optionsStack: if module.initProc != nil: module.initProc.optionsStack + else: @[], options: options, module: module, procDef: procDef, @@ -3036,6 +3039,7 @@ proc processJSCodeGen*(b: PPassContext, n: PNode): PNode = if m.module == nil: internalError(m.config, n.info, "myProcess") let globals = PGlobals(m.graph.backend) var p = newInitProc(globals, m) + m.initProc = p p.unique = globals.unique genModule(p, n) p.g.code.add(p.locals) diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim index f2779ea70fc7..6d7eade91fc4 100644 --- a/tests/pragmas/tpush.nim +++ b/tests/pragmas/tpush.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c js" +""" + # test the new pragmas {.push warnings: off, hints: off.} @@ -25,3 +29,12 @@ proc foo(x: string, y: int, res: int) = foo("", 0, 48) foo("abc", 40, 51) + +# bug #22362 +{.push staticBoundChecks: on.} +proc main(): void = + {.pop.} + discard + {.push staticBoundChecks: on.} + +main() From b40da812f7aa590ed16df54a492684c228320549 Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 2 Aug 2023 20:08:51 +0800 Subject: [PATCH 100/347] fix #22173 `sink` paramers not moved into closure (refc) (#22359) * use genRefAssign when assign to sink string * add test case --- compiler/ccgexprs.nim | 15 +++++++++------ tests/gc/t22173.nim | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 tests/gc/t22173.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 712b874d93d8..8874f54ae3a5 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -342,12 +342,15 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc]) elif dest.storage == OnHeap: - # we use a temporary to care for the dreaded self assignment: - var tmp: TLoc - getTemp(p, ty, tmp) - linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", - [dest.rdLoc, src.rdLoc, tmp.rdLoc]) - linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) + if dest.lode.typ.kind == tySink: + genRefAssign(p, dest, src) + else: + # we use a temporary to care for the dreaded self assignment: + var tmp: TLoc + getTemp(p, ty, tmp) + linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", + [dest.rdLoc, src.rdLoc, tmp.rdLoc]) + linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", [addrLoc(p.config, dest), rdLoc(src)]) diff --git a/tests/gc/t22173.nim b/tests/gc/t22173.nim new file mode 100644 index 000000000000..3fa3cc5038c9 --- /dev/null +++ b/tests/gc/t22173.nim @@ -0,0 +1,20 @@ +discard """ + cmd: '''nim c --gc:refc -r $file''' +""" +const Memo = 100 * 1024 + +proc fff(v: sink string): iterator(): char = + return iterator(): char = + for c in v: + yield c + +var tmp = newString(Memo) + +let iter = fff(move(tmp)) + +while true: + let v = iter() + if finished(iter): + break + +doAssert getOccupiedMem() < Memo * 3 From 6b913b4741df8c80b2d930643f6dc01300fc1e1e Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 4 Aug 2023 01:56:05 +0800 Subject: [PATCH 101/347] =?UTF-8?q?Revert=20"fix=20#22173=20`sink`=20param?= =?UTF-8?q?ers=20not=20moved=20into=20closure=20(refc)=20(#22=E2=80=A6=20(?= =?UTF-8?q?#22376)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "fix #22173 `sink` paramers not moved into closure (refc) (#22359)" This reverts commit b40da812f7aa590ed16df54a492684c228320549. --- compiler/ccgexprs.nim | 15 ++++++--------- tests/gc/t22173.nim | 20 -------------------- 2 files changed, 6 insertions(+), 29 deletions(-) delete mode 100644 tests/gc/t22173.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8874f54ae3a5..712b874d93d8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -342,15 +342,12 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(p.config): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc]) elif dest.storage == OnHeap: - if dest.lode.typ.kind == tySink: - genRefAssign(p, dest, src) - else: - # we use a temporary to care for the dreaded self assignment: - var tmp: TLoc - getTemp(p, ty, tmp) - linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", - [dest.rdLoc, src.rdLoc, tmp.rdLoc]) - linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) + # we use a temporary to care for the dreaded self assignment: + var tmp: TLoc + getTemp(p, ty, tmp) + linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", + [dest.rdLoc, src.rdLoc, tmp.rdLoc]) + linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) else: linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n", [addrLoc(p.config, dest), rdLoc(src)]) diff --git a/tests/gc/t22173.nim b/tests/gc/t22173.nim deleted file mode 100644 index 3fa3cc5038c9..000000000000 --- a/tests/gc/t22173.nim +++ /dev/null @@ -1,20 +0,0 @@ -discard """ - cmd: '''nim c --gc:refc -r $file''' -""" -const Memo = 100 * 1024 - -proc fff(v: sink string): iterator(): char = - return iterator(): char = - for c in v: - yield c - -var tmp = newString(Memo) - -let iter = fff(move(tmp)) - -while true: - let v = iter() - if finished(iter): - break - -doAssert getOccupiedMem() < Memo * 3 From 8d8d75706cddc40ff00b163ebd9aa2728afdf7ef Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Thu, 3 Aug 2023 22:49:52 +0200 Subject: [PATCH 102/347] Add experimental inferGenericTypes switch (#22317) * Infer generic bindings * Simple test * Add t * Allow it to work for templates too * Fix some builds by putting bindings in a template * Fix builtins * Slightly more exotic seq test * Test value-based generics using array * Pass expectedType into buildBindings * Put buildBindings into a proc * Manual entry * Remove leftover ` * Improve language used in the manual * Experimental flag and fix basic constructors * Tiny commend cleanup * Move to experimental manual * Use 'kind' so tuples continue to fail like before * Explicitly disallow tuples * Table test and document tuples * Test type reduction * Disable inferGenericTypes check for CI tests * Remove tuple info in manual * Always reduce types. Testing CI * Fixes * Ignore tyGenericInst * Prevent binding already bound generic params * tyUncheckedArray * Few more types * Update manual and check for flag again * Update tests/generics/treturn_inference.nim * var candidate, remove flag check again for CI * Enable check once more --------- Co-authored-by: SirOlaf <> Co-authored-by: Andreas Rumpf --- compiler/options.nim | 3 +- compiler/semcall.nim | 64 +++++++++++- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 10 +- doc/manual_experimental.md | 81 ++++++++++++++++ tests/generics/treturn_inference.nim | 139 +++++++++++++++++++++++++++ 6 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 tests/generics/treturn_inference.nim diff --git a/compiler/options.nim b/compiler/options.nim index 8286a575df74..5b61cb049cc4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -220,7 +220,8 @@ type unicodeOperators, # deadcode flexibleOptionalParams, strictDefs, - strictCaseObjects + strictCaseObjects, + inferGenericTypes LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d2460ab06df7..f0d0f648a67a 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -562,8 +562,61 @@ proc getCallLineInfo(n: PNode): TLineInfo = discard result = n.info -proc semResolvedCall(c: PContext, x: TCandidate, - n: PNode, flags: TExprFlags): PNode = +proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = + ## Helper proc to inherit bound generic parameters from expectedType into x. + ## Does nothing if 'inferGenericTypes' isn't in c.features + if inferGenericTypes notin c.features: return + if expectedType == nil or x.callee[0] == nil: return # required for inference + + var + flatUnbound: seq[PType] + flatBound: seq[PType] + # seq[(result type, expected type)] + var typeStack = newSeq[(PType, PType)]() + + template stackPut(a, b) = + ## skips types and puts the skipped version on stack + # It might make sense to skip here one by one. It's not part of the main + # type reduction because the right side normally won't be skipped + const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass } + let + x = a.skipTypes(toSkip) + y = if a.kind notin toSkip: b + else: b.skipTypes(toSkip) + typeStack.add((x, y)) + + stackPut(x.callee[0], expectedType) + + while typeStack.len() > 0: + let (t, u) = typeStack.pop() + if t == u or t == nil or u == nil or t.kind == tyAnything or u.kind == tyAnything: + continue + case t.kind + of ConcreteTypes, tyGenericInvocation, tyUncheckedArray: + # nested, add all the types to stack + let + startIdx = if u.kind in ConcreteTypes: 0 else: 1 + endIdx = min(u.sons.len() - startIdx, t.sons.len()) + + for i in startIdx ..< endIdx: + # early exit with current impl + if t[i] == nil or u[i] == nil: return + stackPut(t[i], u[i]) + of tyGenericParam: + if x.bindings.idTableGet(t) != nil: return + + # fully reduced generic param, bind it + if t notin flatUnbound: + flatUnbound.add(t) + flatBound.add(u) + else: + discard + for i in 0 ..< flatUnbound.len(): + x.bindings.idTablePut(flatUnbound[i], flatBound[i]) + +proc semResolvedCall(c: PContext, x: var TCandidate, + n: PNode, flags: TExprFlags; + expectedType: PType = nil): PNode = assert x.state == csMatch var finalCallee = x.calleeSym let info = getCallLineInfo(n) @@ -583,10 +636,12 @@ proc semResolvedCall(c: PContext, x: TCandidate, if x.calleeSym.magic in {mArrGet, mArrPut}: finalCallee = x.calleeSym else: + c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) else: # For macros and templates, the resolved generic params # are added as normal params. + c.inheritBindings(x, expectedType) for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind of skConst: @@ -615,7 +670,8 @@ proc tryDeref(n: PNode): PNode = result.add n proc semOverloadedCall(c: PContext, n, nOrig: PNode, - filter: TSymKinds, flags: TExprFlags): PNode = + filter: TSymKinds, flags: TExprFlags; + expectedType: PType = nil): PNode = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: @@ -625,7 +681,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, message(c.config, n.info, hintUserRaw, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) - result = semResolvedCall(c, r, n, flags) + result = semResolvedCall(c, r, n, flags, expectedType) else: if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: result = semGenericStmt(c, n) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index ddd8d33efd35..a85f8e6387db 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -135,7 +135,7 @@ type semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, - filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.} + filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c6be3e833c35..398424bbf4e9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -952,17 +952,17 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = result = fixupTypeAfterEval(c, result, a) proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, - flags: TExprFlags): PNode = + flags: TExprFlags; expectedType: PType = nil): PNode = if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction # to 'skIterator' anymore; skIterator is preferred in sigmatch already # for typeof support. # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType) else: result = semOverloadedCall(c, n, nOrig, - {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType) if result != nil: if result[0].kind != nkSym: @@ -1138,7 +1138,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = # this seems to be a hotspot in the compiler! let nOrig = n.copyTree #semLazyOpAux(c, n) - result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags) + result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags, expectedType) if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType) else: result = errorNode(c, n) @@ -3120,7 +3120,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) else: result = semMagic(c, n, s, flags, expectedType) of skProc, skFunc, skMethod, skConverter, skIterator: - if s.magic == mNone: result = semDirectOp(c, n, flags) + if s.magic == mNone: result = semDirectOp(c, n, flags, expectedType) else: result = semMagic(c, n, s, flags, expectedType) else: #liMessage(n.info, warnUser, renderTree(n)); diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 602ca46a588d..4ee035b65d1b 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -124,6 +124,87 @@ would not match the type of the variable, and an error would be given. The extent of this varies, but there are some notable special cases. + +Inferred generic parameters +--------------------------- + +In expressions making use of generic procs or templates, the expected +(unbound) types are often able to be inferred based on context. +This feature has to be enabled via `{.experimental: "inferGenericTypes".}` + + ```nim test = "nim c $1" + {.experimental: "inferGenericTypes".} + + import std/options + + var x = newSeq[int](1) + # Do some work on 'x'... + + # Works! + # 'x' is 'seq[int]' so 'newSeq[int]' is implied + x = newSeq(10) + + # Works! + # 'T' of 'none' is bound to the 'T' of 'noneProducer', passing it along. + # Effectively 'none.T = noneProducer.T' + proc noneProducer[T](): Option[T] = none() + let myNone = noneProducer[int]() + + # Also works + # 'myOtherNone' binds its 'T' to 'float' and 'noneProducer' inherits it + # noneProducer.T = myOtherNone.T + let myOtherNone: Option[float] = noneProducer() + + # Works as well + # none.T = myOtherOtherNone.T + let myOtherOtherNone: Option[int] = none() + ``` + +This is achieved by reducing the types on the lhs and rhs until the *lhs* is left with only types such as `T`. +While lhs and rhs are reduced together, this does *not* mean that the *rhs* will also only be left +with a flat type `Z`, it may be of the form `MyType[Z]`. + +After the types have been reduced, the types `T` are bound to the types that are left on the rhs. + +If bindings *cannot be inferred*, compilation will fail and manual specification is required. + +An example for *failing inference* can be found when passing a generic expression +to a function/template call: + + ```nim test = "nim c $1" status = 1 + {.experimental: "inferGenericTypes".} + + proc myProc[T](a, b: T) = discard + + # Fails! Unable to infer that 'T' is supposed to be 'int' + myProc(newSeq[int](), newSeq(1)) + + # Works! Manual specification of 'T' as 'int' necessary + myProc(newSeq[int](), newSeq[int](1)) + ``` + +Combination of generic inference with the `auto` type is also unsupported: + + ```nim test = "nim c $1" status = 1 + {.experimental: "inferGenericTypes".} + + proc produceValue[T]: auto = default(T) + let a: int = produceValue() # 'auto' cannot be inferred here + ``` + +**Note**: The described inference does not permit the creation of overrides based on +the return type of a procedure. It is a mapping mechanism that does not attempt to +perform deeper inference, nor does it modify what is a valid override. + + ```nim test = "nim c $1" status = 1 + # Doesn't affect the following code, it is invalid either way + {.experimental: "inferGenericTypes".} + + proc a: int = 0 + proc a: float = 1.0 # Fails! Invalid code and not recommended + ``` + + Sequence literals ----------------- diff --git a/tests/generics/treturn_inference.nim b/tests/generics/treturn_inference.nim new file mode 100644 index 000000000000..05d38cef48b5 --- /dev/null +++ b/tests/generics/treturn_inference.nim @@ -0,0 +1,139 @@ + +{.experimental: "inferGenericTypes".} + +import std/tables + +block: + type + MyOption[T, Z] = object + x: T + y: Z + + proc none[T, Z](): MyOption[T, Z] = + when T is int: + result.x = 22 + when Z is float: + result.y = 12.0 + + proc myGenericProc[T, Z](): MyOption[T, Z] = + none() # implied by return type + + let a = myGenericProc[int, float]() + doAssert a.x == 22 + doAssert a.y == 12.0 + + let b: MyOption[int, float] = none() # implied by type of b + doAssert b.x == 22 + doAssert b.y == 12.0 + +# Simple template based result with inferred type for errors +block: + type + ResultKind {.pure.} = enum + Ok + Err + + Result[T] = object + case kind: ResultKind + of Ok: + data: T + of Err: + errmsg: cstring + + template err[T](msg: static cstring): Result[T] = + Result[T](kind : ResultKind.Err, errmsg : msg) + + proc testproc(): Result[int] = + err("Inferred error!") # implied by proc return + let r = testproc() + doAssert r.kind == ResultKind.Err + doAssert r.errmsg == "Inferred error!" + +# Builtin seq +block: + let x: seq[int] = newSeq(1) + doAssert x is seq[int] + doAssert x.len() == 1 + + type + MyType[T, Z] = object + x: T + y: Z + + let y: seq[MyType[int, float]] = newSeq(2) + doAssert y is seq[MyType[int, float]] + doAssert y.len() == 2 + + let z = MyType[seq[float], string]( + x : newSeq(3), + y : "test" + ) + doAssert z.x is seq[float] + doAssert z.x.len() == 3 + doAssert z.y is string + doAssert z.y == "test" + +# array +block: + proc giveArray[N, T](): array[N, T] = + for i in 0 .. N.high: + result[i] = i + var x: array[2, int] = giveArray() + doAssert x == [0, 1] + +# tuples +block: + proc giveTuple[T, Z]: (T, Z, T) = discard + let x: (int, float, int) = giveTuple() + doAssert x is (int, float, int) + doAssert x == (0, 0.0, 0) + + proc giveNamedTuple[T, Z]: tuple[a: T, b: Z] = discard + let y: tuple[a: int, b: float] = giveNamedTuple() + doAssert y is (int, float) + doAssert y is tuple[a: int, b: float] + doAssert y == (0, 0.0) + + proc giveNestedTuple[T, Z]: ((T, Z), Z) = discard + let z: ((int, float), float) = giveNestedTuple() + doAssert z is ((int, float), float) + doAssert z == ((0, 0.0), 0.0) + + # nesting inside a generic type + type MyType[T] = object + x: T + let a = MyType[(int, MyType[float])](x : giveNamedTuple()) + doAssert a.x is (int, MyType[float]) + + +# basic constructors +block: + type MyType[T] = object + x: T + + proc giveValue[T](): T = + when T is int: + 12 + else: + default(T) + + let x = MyType[int](x : giveValue()) + doAssert x.x is int + doAssert x.x == 12 + + let y = MyType[MyType[float]](x : MyType[float](x : giveValue())) + doAssert y.x is MyType[float] + doAssert y.x.x is float + doAssert y.x.x == 0.0 + + # 'MyType[float]' is bound to 'T' directly + # instead of mapping 'T' to 'float' + let z = MyType[MyType[float]](x : giveValue()) + doAssert z.x is MyType[float] + doAssert z.x.x == 0.0 + + type Foo = object + x: Table[int, float] + + let a = Foo(x: initTable()) + doAssert a.x is Table[int, float] \ No newline at end of file From 14bc3f32683c87f971bf23ae30d500dc89cdebb8 Mon Sep 17 00:00:00 2001 From: awr1 <41453959+awr1@users.noreply.github.com> Date: Thu, 3 Aug 2023 14:06:30 -0700 Subject: [PATCH 103/347] Allow `libffi` to work via `koch boot` (#22322) * Divert libffi from nimble path, impl into koch * Typo in koch * Update options.nim comment * Fix CI Test * Update changelog * Clarify libffi nimble comment * Future pending changelog --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- changelogs/changelog_2_2_0.md | 14 ++++++++++++ compiler/evalffi.nim | 2 +- compiler/options.nim | 2 +- koch.nim | 42 ++++++++++++++++++++++++----------- 4 files changed, 45 insertions(+), 15 deletions(-) create mode 100644 changelogs/changelog_2_2_0.md diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md new file mode 100644 index 000000000000..97b9e6c05221 --- /dev/null +++ b/changelogs/changelog_2_2_0.md @@ -0,0 +1,14 @@ +# v2.2.1 - 2023-mm-dd + +## Changes affecting backward compatibility + +## Standard library additions and changes + +## Language changes + +## Compiler changes + +## Tool changes + +- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow. + diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 0577619b8926..0112aebb914e 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -11,7 +11,7 @@ import ast, types, options, tables, dynlib, msgs, lineinfos from os import getAppFilename -import pkg/libffi +import libffi/libffi when defined(windows): const libcDll = "msvcrt.dll" diff --git a/compiler/options.nim b/compiler/options.nim index 5b61cb049cc4..5a862ca4b055 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -209,7 +209,7 @@ type codeReordering, compiletimeFFI, ## This requires building nim with `-d:nimHasLibFFI` - ## which itself requires `nimble install libffi`, see #10150 + ## which itself requires `koch installdeps libffi`, see #10150 ## Note: this feature can't be localized with {.push.} vmopsDanger, strictFuncs, diff --git a/koch.nim b/koch.nim index a10e01dbbe98..8e94b51507c7 100644 --- a/koch.nim +++ b/koch.nim @@ -85,6 +85,9 @@ Boot options: -d:leanCompiler produce a compiler without JS codegen or documentation generator in order to use less RAM for bootstrapping + -d:nimHasLibFFI adds FFI support for allowing compile-time VM to + interface with native functions (experimental, + requires prior `koch installdeps libffi`) Commands for core developers: runCI runs continuous integration (CI), e.g. from Github Actions @@ -278,6 +281,22 @@ proc install(args: string) = geninstall() exec("sh ./install.sh $#" % args) +proc installDeps(dep: string, commit = "") = + # the hashes/urls are version controlled here, so can be changed seamlessly + # and tied to a nim release (mimicking git submodules) + var commit = commit + case dep + of "tinyc": + if commit.len == 0: commit = "916cc2f94818a8a382dd8d4b8420978816c1dfb3" + cloneDependency(distDir, "https://github.com/timotheecour/nim-tinyc-archive", commit) + of "libffi": + # technically a nimble package, however to play nicely with --noNimblePath, + # let's just clone it wholesale: + if commit.len == 0: commit = "bb2bdaf1a29a4bff6fbd8ae4695877cbb3ec783e" + cloneDependency(distDir, "https://github.com/Araq/libffi", commit) + else: doAssert false, "unsupported: " & dep + # xxx: also add linenoise, niminst etc, refs https://github.com/nim-lang/RFCs/issues/206 + # -------------- boot --------------------------------------------------------- proc findStartNim: string = @@ -323,6 +342,10 @@ proc boot(args: string, skipIntegrityCheck: bool) = if not dirExists("dist/checksums"): bundleChecksums(false) + let usingLibFFI = "nimHasLibFFI" in args + if usingLibFFI and not dirExists("dist/libffi"): + installDeps("libffi") + let nimStart = findStartNim().quoteShell() let times = 2 - ord(skipIntegrityCheck) for i in 0..times: @@ -334,6 +357,10 @@ proc boot(args: string, skipIntegrityCheck: bool) = if i == 0: nimi = nimStart extraOption.add " --skipUserCfg --skipParentCfg -d:nimKochBootstrap" + + # --noNimblePath precludes nimble packages as dependencies to the compiler, + # so libffi is not "installed as a nimble package" + if usingLibFFI: extraOption.add " --path:./dist" # The configs are skipped for bootstrap # (1st iteration) to prevent newer flags from breaking bootstrap phase. let ret = execCmdEx(nimStart & " --version") @@ -548,17 +575,6 @@ proc hostInfo(): string = "hostOS: $1, hostCPU: $2, int: $3, float: $4, cpuEndian: $5, cwd: $6" % [hostOS, hostCPU, $int.sizeof, $float.sizeof, $cpuEndian, getCurrentDir()] -proc installDeps(dep: string, commit = "") = - # the hashes/urls are version controlled here, so can be changed seamlessly - # and tied to a nim release (mimicking git submodules) - var commit = commit - case dep - of "tinyc": - if commit.len == 0: commit = "916cc2f94818a8a382dd8d4b8420978816c1dfb3" - cloneDependency(distDir, "https://github.com/timotheecour/nim-tinyc-archive", commit) - else: doAssert false, "unsupported: " & dep - # xxx: also add linenoise, niminst etc, refs https://github.com/nim-lang/RFCs/issues/206 - proc runCI(cmd: string) = doAssert cmd.len == 0, cmd # avoid silently ignoring echo "runCI: ", cmd @@ -602,11 +618,11 @@ proc runCI(cmd: string) = block: # nimHasLibFFI: when defined(posix): # windows can be handled in future PR's - execFold("nimble install -y libffi", "nimble install -y libffi") + installDeps("libffi") const nimFFI = "bin/nim.ctffi" # no need to bootstrap with koch boot (would be slower) let backend = if doUseCpp(): "cpp" else: "c" - execFold("build with -d:nimHasLibFFI", "nim $1 -d:release -d:nimHasLibFFI -o:$2 compiler/nim.nim" % [backend, nimFFI]) + execFold("build with -d:nimHasLibFFI", "nim $1 -d:release --noNimblePath -d:nimHasLibFFI --path:./dist -o:$2 compiler/nim.nim" % [backend, nimFFI]) execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend]) execFold("Run nimdoc tests", "nim r nimdoc/tester") From d37b620757c0a4987ee349f246df30def8613b9b Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Fri, 4 Aug 2023 05:29:48 +0200 Subject: [PATCH 104/347] Make `repr(HSlice)` always available (#22332) Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- lib/system.nim | 10 ++++++++++ lib/system/repr_v2.nim | 10 ---------- tests/stdlib/trepr.nim | 6 ++++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 25133fca50ab..1bf1c5ccb406 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2405,6 +2405,16 @@ when defined(nimV2): import system/repr_v2 export repr_v2 +proc repr*[T, U](x: HSlice[T, U]): string = + ## Generic `repr` operator for slices that is lifted from the components + ## of `x`. Example: + ## + ## .. code-block:: Nim + ## $(1 .. 5) == "1 .. 5" + result = repr(x.a) + result.add(" .. ") + result.add(repr(x.b)) + when hasAlloc or defined(nimscript): proc insert*(x: var string, item: string, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index e9a6596fd3e5..3a40ac5ad7b2 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -177,16 +177,6 @@ proc repr*[T](x: seq[T]): string = ## $(@[23, 45]) == "@[23, 45]" collectionToRepr(x, "@[", ", ", "]") -proc repr*[T, U](x: HSlice[T, U]): string = - ## Generic `repr` operator for slices that is lifted from the components - ## of `x`. Example: - ## - ## .. code-block:: Nim - ## $(1 .. 5) == "1 .. 5" - result = repr(x.a) - result.add(" .. ") - result.add(repr(x.b)) - proc repr*[T, IDX](x: array[IDX, T]): string = ## Generic `repr` operator for arrays that is lifted from the components. collectionToRepr(x, "[", ", ", "]") diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index a8c62ea55b8e..3956b98f9522 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -40,7 +40,7 @@ template main() = #[ BUG: --gc:arc returns `"abc"` - regular gc returns with address, e.g. 0x1068aae60"abc", but only + regular gc returns with address, e.g. 0x1068aae60"abc", but only for c,cpp backends (not js, vm) ]# block: @@ -293,7 +293,7 @@ func fn2(): int = *a: b do: c - + doAssert a == """foo(a, b, (c, d)): e f @@ -322,5 +322,7 @@ else: do: c""" + doAssert repr(1..2) == "1 .. 2" + static: main() main() From fb7acd66001a5f1bc018e1e1e9b1ebc792462e50 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 4 Aug 2023 15:08:41 +0800 Subject: [PATCH 105/347] follow up #22322; fixes changelog (#22381) --- changelog.md | 4 ++-- changelogs/changelog_2_2_0.md | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 100d12fd7207..b4b8ca532da9 100644 --- a/changelog.md +++ b/changelog.md @@ -24,7 +24,7 @@ ## Compiler changes - - ## Tool changes +- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow. + diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md index 97b9e6c05221..341f41045a7f 100644 --- a/changelogs/changelog_2_2_0.md +++ b/changelogs/changelog_2_2_0.md @@ -10,5 +10,3 @@ ## Tool changes -- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow. - From 7c2a2c8dc810a837b93ee0e8bcaf6d8969f5a54a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 4 Aug 2023 18:00:00 +0800 Subject: [PATCH 106/347] fixes a typo in the manual (#22383) ref https://github.com/nim-lang/Nim/commit/0d3bde95f578576d2e84d422d5694ee0e0055cbc#commitcomment-122093273 --- doc/manual.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.md b/doc/manual.md index 45eb8fef562d..fba905bb9c15 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -5517,7 +5517,7 @@ type Foo[T] = object proc p[H;T: Foo[H]](param: T): H ``` -A constraint definition may have more than one symbol defined by seperating each definition by +A constraint definition may have more than one symbol defined by separating each definition by a `;`. Notice how `T` is composed of `H` and the return type of `p` is defined as `H`. When this generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and the return type of `p` will be bound to the same concrete type used to define `H`. From 3efabd3ec669914ad2bb42a614f7277caf662562 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 4 Aug 2023 20:21:36 +1000 Subject: [PATCH 107/347] Fix crash when using uninstantiated generic (#22379) * Add test case * Add in a bounds check when accessing generic types Removes idnex out of bounds exception when comparing a generic that isn't fully instantiated --- compiler/sigmatch.nim | 2 ++ tests/generics/tuninstantiated_failure.nim | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/generics/tuninstantiated_failure.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4aa51977ab65..d1b3b5dfb487 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1536,6 +1536,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if x.kind == tyGenericInvocation: if f[0] == x[0]: for i in 1..= x.len: return let tr = typeRel(c, f[i], x[i], flags) if tr <= isSubtype: return result = isGeneric diff --git a/tests/generics/tuninstantiated_failure.nim b/tests/generics/tuninstantiated_failure.nim new file mode 100644 index 000000000000..f09b115d65a0 --- /dev/null +++ b/tests/generics/tuninstantiated_failure.nim @@ -0,0 +1,16 @@ +discard """ +cmd: "nim check $file" +""" + +type + Test[T, K] = object + name: string + Something = Test[int] + +func `[]`[T, K](x: var Test[T, K], idx: int): var Test[T, K] = + x + +var b: Something +# Should give a type-mismatch since Something isn't a valid Test +b[0].name = "Test" #[tt.Error + ^ type mismatch]# From 26f183043f9e58eb4954d50a5d130d8684909936 Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 4 Aug 2023 19:35:43 +0800 Subject: [PATCH 108/347] fix #20883 Unspecified generic on default value segfaults the compiler (#21172) * fix #20883 Unspecified generic on default value segfaults the compiler * fallback to isGeneric * change to closer error * Update t20883.nim --- compiler/semexprs.nim | 3 +++ compiler/sigmatch.nim | 5 +++++ tests/misc/t20883.nim | 12 ++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/misc/t20883.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 398424bbf4e9..b7fc7a9bd808 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -238,6 +238,9 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = result = convNotInRange else: # we use d, s here to speed up that operation a bit: + if d.kind == tyFromExpr: + result = convNotLegal + return case cmpTypes(c, d, s) of isNone, isGeneric: if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct): diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d1b3b5dfb487..12cc1fcb127c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -819,6 +819,8 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, # This proc is used to evaluate such static expressions. let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil, allowMetaTypes = allowUnresolved) + if instantiated.kind in nkCallKinds: + return nil result = c.c.semExpr(c.c, instantiated) proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = @@ -1887,6 +1889,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # fix the expression, so it contains the already instantiated types if f.n == nil or f.n.kind == nkEmpty: return isGeneric let reevaluated = tryResolvingStaticExpr(c, f.n) + if reevaluated == nil: + result = isNone + return case reevaluated.typ.kind of tyTypeDesc: result = typeRel(c, a, reevaluated.typ.base, flags) diff --git a/tests/misc/t20883.nim b/tests/misc/t20883.nim new file mode 100644 index 000000000000..d98feaa14102 --- /dev/null +++ b/tests/misc/t20883.nim @@ -0,0 +1,12 @@ +discard """ + action: reject + errormsg: "type mismatch: got but expected 'typeof(U(0.000001))'" + line: 8 + column: 22 +""" + +proc foo*[U](x: U = U(1e-6)) = + echo x + +foo[float]() +foo() From 73a29d72e347817a239f097c8185842e5bdca149 Mon Sep 17 00:00:00 2001 From: norrath-hero-cn <73905273+norrath-hero-cn@users.noreply.github.com> Date: Sat, 5 Aug 2023 01:59:05 +0800 Subject: [PATCH 109/347] fixes AddressSanitizer: global-buffer-overflow in getAppFilename on windows 10 (#22380) fixes AddressSanitizer: global-buffer-overflow --- lib/pure/os.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 434fc3a26e01..13c6d6113ad0 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -638,14 +638,14 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW # /proc//path/a.out (complete pathname) when defined(windows): var bufsize = int32(MAX_PATH) - var buf = newWideCString("", bufsize) + var buf = newWideCString(bufsize) while true: var L = getModuleFileNameW(0, buf, bufsize) if L == 0'i32: result = "" # error! break elif L > bufsize: - buf = newWideCString("", L) + buf = newWideCString(L) bufsize = L else: result = buf$L From db435a4a797adbbd4dd42edf89267902c0b2e34f Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Sat, 5 Aug 2023 03:00:43 +0900 Subject: [PATCH 110/347] Fix searchExtPos so that it returns -1 when the path is not a file ext (#22245) * Fix searchExtPos so that it returns -1 when the path is not a file ext * fix comparision expression * Remove splitDrive from searchExtPos --- lib/std/private/ospaths2.nim | 21 +++++++++++++++++---- tests/stdlib/tos.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 612003023cb5..18a01b10495b 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -584,15 +584,28 @@ proc searchExtPos*(path: string): int = assert searchExtPos("c.nim") == 1 assert searchExtPos("a/b/c.nim") == 5 assert searchExtPos("a.b.c.nim") == 5 + assert searchExtPos(".nim") == -1 + assert searchExtPos("..nim") == -1 + assert searchExtPos("a..nim") == 2 - # BUGFIX: do not search until 0! .DS_Store is no file extension! + # Unless there is any char that is not `ExtSep` before last `ExtSep` in the file name, + # it is not a file extension. + const DirSeps = when doslikeFileSystem: {DirSep, AltSep, ':'} else: {DirSep, AltSep} result = -1 - for i in countdown(len(path)-1, 1): + var i = path.high + while i >= 1: if path[i] == ExtSep: + break + elif path[i] in DirSeps: + return -1 # do not skip over path + dec i + + for j in countdown(i - 1, 0): + if path[j] in DirSeps: + return -1 + elif path[j] != ExtSep: result = i break - elif path[i] in {DirSep, AltSep}: - break # do not skip over path proc splitFile*(path: string): tuple[dir, name, ext: string] {. noSideEffect, rtl, extern: "nos$1".} = diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index c2822d2707b7..ad34e479a91e 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -830,3 +830,35 @@ block: # isValidFilename doAssert isValidFilename("ux.bat") doAssert isValidFilename("nim.nim") doAssert isValidFilename("foo.log") + +block: # searchExtPos + doAssert "foo.nim".searchExtPos == 3 + doAssert "/foo.nim".searchExtPos == 4 + doAssert "".searchExtPos == -1 + doAssert "/".searchExtPos == -1 + doAssert "a.b/foo".searchExtPos == -1 + doAssert ".".searchExtPos == -1 + doAssert "foo.".searchExtPos == 3 + doAssert "foo..".searchExtPos == 4 + doAssert "..".searchExtPos == -1 + doAssert "...".searchExtPos == -1 + doAssert "./".searchExtPos == -1 + doAssert "../".searchExtPos == -1 + doAssert "/.".searchExtPos == -1 + doAssert "/..".searchExtPos == -1 + doAssert ".b".searchExtPos == -1 + doAssert "..b".searchExtPos == -1 + doAssert "/.b".searchExtPos == -1 + doAssert "a/.b".searchExtPos == -1 + doAssert ".a.b".searchExtPos == 2 + doAssert "a/.b.c".searchExtPos == 4 + doAssert "a/..b".searchExtPos == -1 + doAssert "a/b..c".searchExtPos == 4 + + when doslikeFileSystem: + doAssert "c:a.b".searchExtPos == 3 + doAssert "c:.a".searchExtPos == -1 + doAssert r"c:\.a".searchExtPos == -1 + doAssert "c:..a".searchExtPos == -1 + doAssert r"c:\..a".searchExtPos == -1 + doAssert "c:.a.b".searchExtPos == 4 From 873eaa3f65f9ef96f3dc4430e8938d273f04f8e9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 4 Aug 2023 22:52:31 +0200 Subject: [PATCH 111/347] compiler/llstream: modern code for llstream (#22385) --- compiler/llstream.nim | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 004d990faf29..bad28eb12c4c 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -40,33 +40,22 @@ type PLLStream* = ref TLLStream -proc llStreamOpen*(data: string): PLLStream = - new(result) - result.s = data - result.kind = llsString +proc llStreamOpen*(data: sink string): PLLStream = + PLLStream(kind: llsString, s: data) proc llStreamOpen*(f: File): PLLStream = - new(result) - result.f = f - result.kind = llsFile + PLLStream(kind: llsFile, f: f) proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream = - new(result) - result.kind = llsFile + result = PLLStream(kind: llsFile) if not open(result.f, filename.string, mode): result = nil proc llStreamOpen*(): PLLStream = - new(result) - result.kind = llsNone + PLLStream(kind: llsNone) proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream = - new(result) - result.kind = llsStdIn - result.s = "" - result.lineOffset = -1 - result.repl = r - result.onPrompt = onPrompt + PLLStream(kind: llsStdIn, s: "", lineOffset: -1, repl: r, onPrompt: onPrompt) proc llStreamClose*(s: PLLStream) = case s.kind From e15e19308ea3f85ee746cd2946f9acde94b71e34 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sat, 5 Aug 2023 18:38:46 +0200 Subject: [PATCH 112/347] Revert adding generic `V: Ordinal` parameter to `succ`, `pred`, `inc`, `dec` (#22328) * Use `int` in `digitsutils`, `dragonbox`, `schubfach` * Fix error message --- lib/std/private/digitsutils.nim | 4 ++-- lib/std/private/dragonbox.nim | 10 +++++----- lib/std/private/schubfach.nim | 8 ++++---- lib/system/arithmetics.nim | 10 +++++----- tests/varres/tprevent_forloopvar_mutations.nim | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index e051e5218349..55ace35001d3 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -33,8 +33,8 @@ proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} buf[pos+1] = digits100[2 * digits + 1] #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) -proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = - return trailingZeros100[digits.int8] +proc trailingZeros2Digits*(digits: uint32): int {.inline.} = + trailingZeros100[digits] when defined(js): proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".} diff --git a/lib/std/private/dragonbox.nim b/lib/std/private/dragonbox.nim index e39ffd9a3a15..85ffea84a29d 100644 --- a/lib/std/private/dragonbox.nim +++ b/lib/std/private/dragonbox.nim @@ -1052,7 +1052,7 @@ when false: proc memset(x: cstring; ch: char; L: int) {.importc, nodecl.} proc memmove(a, b: cstring; L: int) {.importc, nodecl.} -proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int32 {.inline.} = +proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int {.inline.} = dragonbox_Assert(digits >= 1) dragonbox_Assert(digits <= 99999999'u32) let q: uint32 = digits div 10000 @@ -1070,12 +1070,12 @@ proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: u utoa2Digits(buf, pos + 6, rL) return trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0) -proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int32 {.inline.} = +proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int {.inline.} = var pos = pos var output64 = output64 - var tz: int32 = 0 + var tz = 0 ## number of trailing zeros removed. - var nd: int32 = 0 + var nd = 0 ## number of decimal digits processed. ## At most 17 digits remaining if output64 >= 100000000'u64: @@ -1220,7 +1220,7 @@ proc formatDigits*[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint ## dE+123 or d.igitsE+123 decimalDigitsPosition = 1 var digitsEnd = pos + int(decimalDigitsPosition + numDigits) - let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) + let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits) dec(digitsEnd, tz) dec(numDigits, tz) ## decimal_exponent += tz; // => decimal_point unchanged. diff --git a/lib/std/private/schubfach.nim b/lib/std/private/schubfach.nim index 194fb4bfab26..b8c85d2bc7a0 100644 --- a/lib/std/private/schubfach.nim +++ b/lib/std/private/schubfach.nim @@ -244,12 +244,12 @@ proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal ## ToChars ## ================================================================================================== -proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int32 {.inline.} = +proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int {.inline.} = var output = output var pos = pos - var tz: int32 = 0 + var tz = 0 ## number of trailing zeros removed. - var nd: int32 = 0 + var nd = 0 ## number of decimal digits processed. ## At most 9 digits remaining if output >= 10000: @@ -355,7 +355,7 @@ proc formatDigits[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint3 ## dE+123 or d.igitsE+123 decimalDigitsPosition = 1 var digitsEnd = pos + decimalDigitsPosition + numDigits - let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) + let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits) dec(digitsEnd, tz) dec(numDigits, tz) ## decimal_exponent += tz; // => decimal_point unchanged. diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index a1cb935ce0e1..fa7ca784d7e1 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -1,4 +1,4 @@ -proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} = +proc succ*[T: Ordinal](x: T, y: int = 1): T {.magic: "Succ", noSideEffect.} = ## Returns the `y`-th successor (default: 1) of the value `x`. ## ## If such a value does not exist, `OverflowDefect` is raised @@ -7,7 +7,7 @@ proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} = assert succ(5) == 6 assert succ(5, 3) == 8 -proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} = +proc pred*[T: Ordinal](x: T, y: int = 1): T {.magic: "Pred", noSideEffect.} = ## Returns the `y`-th predecessor (default: 1) of the value `x`. ## ## If such a value does not exist, `OverflowDefect` is raised @@ -16,7 +16,7 @@ proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} = assert pred(5) == 4 assert pred(5, 3) == 2 -proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} = +proc inc*[T: Ordinal](x: var T, y: int = 1) {.magic: "Inc", noSideEffect.} = ## Increments the ordinal `x` by `y`. ## ## If such a value does not exist, `OverflowDefect` is raised or a compile @@ -28,7 +28,7 @@ proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} = inc(i, 3) assert i == 6 -proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} = +proc dec*[T: Ordinal](x: var T, y: int = 1) {.magic: "Dec", noSideEffect.} = ## Decrements the ordinal `x` by `y`. ## ## If such a value does not exist, `OverflowDefect` is raised or a compile @@ -93,7 +93,7 @@ proc `*`*(x, y: int16): int16 {.magic: "MulI", noSideEffect.} proc `*`*(x, y: int32): int32 {.magic: "MulI", noSideEffect.} proc `*`*(x, y: int64): int64 {.magic: "MulI", noSideEffect.} -proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} = +proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} = ## Computes the integer division. ## ## This is roughly the same as `math.trunc(x/y).int`. diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim index c9aeb94d8f81..b27c327a9371 100644 --- a/tests/varres/tprevent_forloopvar_mutations.nim +++ b/tests/varres/tprevent_forloopvar_mutations.nim @@ -2,7 +2,7 @@ discard """ errormsg: "type mismatch: got " nimout: '''tprevent_forloopvar_mutations.nim(16, 3) Error: type mismatch: got but expected one of: -proc inc[T, V: Ordinal](x: var T; y: V = 1) +proc inc[T: Ordinal](x: var T; y: int = 1) first type mismatch at position: 1 required type for x: var T: Ordinal but expression 'i' is immutable, not 'var' From 9872453365f0782277aa93045e4c2461d1b7585a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 5 Aug 2023 19:35:37 +0200 Subject: [PATCH 113/347] destructors: better docs [backport:2.0] (#22391) --- doc/destructors.md | 85 +++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/doc/destructors.md b/doc/destructors.md index 3121335145bb..e192fd362cc6 100644 --- a/doc/destructors.md +++ b/doc/destructors.md @@ -41,6 +41,9 @@ written as: for i in 0.. Date: Sun, 6 Aug 2023 01:38:32 +0800 Subject: [PATCH 114/347] Prevent early destruction of gFuns, fixes AddressSanitizer: heap-use-after-free (#22386) Prevent destruction of gFuns before callClosures --- lib/std/exitprocs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index c44eb30d6529..e42397c4cf7c 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -22,7 +22,7 @@ type var gFunsLock: Lock - gFuns: seq[Fun] + gFuns {.cursor.}: seq[Fun] #Intentionally use the cursor to break up the lifetime trace and make it compatible with JS. initLock(gFunsLock) From 7bf7496557d939331193069f56c3faa91d81d9d3 Mon Sep 17 00:00:00 2001 From: Daniel Belmes <3631206+DanielBelmes@users.noreply.github.com> Date: Sat, 5 Aug 2023 11:50:47 -0700 Subject: [PATCH 115/347] fix server caching issue causing Theme failures (#22378) * fix server caching issue causing Theme failures * Fix tester to ignore version cache param * fix case of people using -d:nimTestsNimdocFixup * rsttester needed the same fix --- compiler/docgen.nim | 4 ++-- config/nimdoc.cfg | 4 ++-- nimdoc/rsttester.nim | 6 ++++-- nimdoc/tester.nim | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 5120b522381d..b25a82e4c460 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1723,7 +1723,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = "moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(), "content", content, "author", d.meta[metaAuthor], "version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics, - "deprecationMsg", d.modDeprecationMsg] + "deprecationMsg", d.modDeprecationMsg, "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch] else: code = content result = code @@ -1907,7 +1907,7 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") "title", "Index", "subtitle", "", "tableofcontents", "", "moduledesc", "", "date", getDateStr(), "time", getClockStr(), - "content", content, "author", "", "version", "", "analytics", ""] + "content", content, "author", "", "version", "", "analytics", "", "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch] # no analytics because context is not available try: diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index abe039738f13..9f36e7d1c2ac 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -237,10 +237,10 @@ doc.file = """ - + - +
    diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim index a0bdfca1e16f..be2b56c67831 100644 --- a/nimdoc/rsttester.nim +++ b/nimdoc/rsttester.nim @@ -24,11 +24,13 @@ proc testRst2Html(fixup = false) = let sourceFile = expectedHtml.replace('\\', '/').replace("/expected/", "/source/").replace(".html", ".rst") exec("$1 rst2html $2" % [nimExe, sourceFile]) let producedHtml = expectedHtml.replace('\\', '/').replace("/expected/", "/source/htmldocs/") - if readFile(expectedHtml) != readFile(producedHtml): + let versionCacheParam = "?v=" & $NimMajor & "." & $NimMinor & "." & $NimPatch + let producedFile = readFile(producedHtml).replace(versionCacheParam,"") #remove version cache param used for cache invalidation + if readFile(expectedHtml) != producedFile: echo diffFiles(expectedHtml, producedHtml).output inc failures if fixup: - copyFile(producedHtml, expectedHtml) + writeFile(expectedHtml, producedFile) else: echo "SUCCESS: files identical: ", producedHtml if failures == 0: diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index e94caae7cb9f..0c0be369998f 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -59,16 +59,18 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = echo("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches]) for expected in walkDirRec(prjDir / "expected/", checkDir=true): + let versionCacheParam = "?v=" & $NimMajor & "." & $NimMinor & "." & $NimPatch let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir]) if not fileExists(produced): echo "FAILURE: files not found: ", produced inc failures - elif readFile(expected) != readFile(produced): + let producedFile = readFile(produced).replace(versionCacheParam,"") #remove version cache param used for cache invalidation + if readFile(expected) != producedFile: echo "FAILURE: files differ: ", produced echo diffFiles(expected, produced).output inc failures if fixup: - copyFile(produced, expected) + writeFile(expected, producedFile) else: echo "SUCCESS: files identical: ", produced From b2c3b8f931e680aafaceb5a89bb59c361c81a30a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 6 Aug 2023 08:52:17 +0800 Subject: [PATCH 116/347] introduces online bisecting (#22390) * introduces online bisecting * Update .github/ISSUE_TEMPLATE/bug_report.yml --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8406f607f27e..014513865050 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -71,5 +71,6 @@ body: which should give more context on a compiler crash. - If it's a regression, you can help us by identifying which version introduced the bug, see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions), - or at least try known past releases (eg `choosenim 1.2.0`). + or at least try known past releases (e.g. `choosenim 2.0.0`). The Nim repo also supports online bisecting + via making a comment, which contains a code block starting by `!nim c`, `!nim js` etc. , see [nimrun-action](https://github.com/juancarlospaco/nimrun-action). - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html) From 137d608d7d68a91c99149aa1127dd675ee45f751 Mon Sep 17 00:00:00 2001 From: Bung Date: Sun, 6 Aug 2023 15:21:24 +0800 Subject: [PATCH 117/347] add test for #3907 (#21069) * add test for #3907 --- tests/misc/t3907.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/misc/t3907.nim diff --git a/tests/misc/t3907.nim b/tests/misc/t3907.nim new file mode 100644 index 000000000000..45fc75e8140d --- /dev/null +++ b/tests/misc/t3907.nim @@ -0,0 +1,10 @@ +import std/assertions + +let a = 0 +let b = if false: -1 else: a +doAssert b == 0 + +let c: range[0..high(int)] = 0 +let d = if false: -1 else: c + +doAssert d == 0 From 95c751a9e4d42eb61917684339406d1ff07a4225 Mon Sep 17 00:00:00 2001 From: Bung Date: Sun, 6 Aug 2023 15:46:43 +0800 Subject: [PATCH 118/347] =?UTF-8?q?fix=20#15005;=20[ARC]=20Global=20variab?= =?UTF-8?q?le=20declared=20in=20a=20block=20is=20destroyed=20too=E2=80=A6?= =?UTF-8?q?=20(#22388)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #15005 [ARC] Global variable declared in a block is destroyed too early --- compiler/injectdestructors.nim | 3 ++- tests/global/t15005.nim | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/global/t15005.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 590012806c4f..15f375d28f83 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1153,7 +1153,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy 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): + isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri) and + {sfGlobal, sfPure} <= ri.sym.flags == false: # Rule 3: `=sink`(x, z); wasMoved(z) let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) diff --git a/tests/global/t15005.nim b/tests/global/t15005.nim new file mode 100644 index 000000000000..6395b1dde84f --- /dev/null +++ b/tests/global/t15005.nim @@ -0,0 +1,18 @@ +type + T = ref object + data: string + +template foo(): T = + var a15005 {.global.}: T + once: + a15005 = T(data: "hi") + + a15005 + +proc test() = + var b15005 = foo() + + doAssert b15005.data == "hi" + +test() +test() From f18e4c4050cb59cf828372f89c01d9e80f6516c5 Mon Sep 17 00:00:00 2001 From: Bung Date: Sun, 6 Aug 2023 19:07:01 +0800 Subject: [PATCH 119/347] fix set op related to {sfGlobal, sfPure} (#22393) --- compiler/hlo.nim | 2 +- compiler/injectdestructors.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 1aab9d5fe9df..2e1652f09d21 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -73,7 +73,7 @@ proc hlo(c: PContext, n: PNode): PNode = else: if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and n[0].kind == nkSym and - {sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}: + {sfGlobal, sfPure} <= n[0].sym.flags: # do not optimize 'var g {.global} = re(...)' again! return n result = applyPatterns(c, n) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 15f375d28f83..4463d1d69488 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1154,7 +1154,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy 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) and - {sfGlobal, sfPure} <= ri.sym.flags == false: + not ({sfGlobal, sfPure} <= ri.sym.flags): # Rule 3: `=sink`(x, z); wasMoved(z) let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) From d2b197bdcd91667bbb42763b195d13cd293142fe Mon Sep 17 00:00:00 2001 From: Bung Date: Sun, 6 Aug 2023 19:07:36 +0800 Subject: [PATCH 120/347] Stick search result (#22394) * nimdoc: stick search result inside browser viewport * fix nimdoc.out.css --------- Co-authored-by: Locria Cyber <74560659+locriacyber@users.noreply.github.com> --- doc/nimdoc.css | 8 +++++++- nimdoc/testproject/expected/nimdoc.out.css | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 1417d9eff131..3fb5497ff62c 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -769,7 +769,13 @@ div.search_results { background-color: var(--third-background); margin: 3em; padding: 1em; - border: 1px solid #4d4d4d; } + border: 1px solid #4d4d4d; + position: sticky; + top: 0; + isolation: isolate; + z-index: 1; + max-height: 100vh; + overflow-y: scroll; } div#global-links ul { margin-left: 0; diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 1417d9eff131..3fb5497ff62c 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -769,7 +769,13 @@ div.search_results { background-color: var(--third-background); margin: 3em; padding: 1em; - border: 1px solid #4d4d4d; } + border: 1px solid #4d4d4d; + position: sticky; + top: 0; + isolation: isolate; + z-index: 1; + max-height: 100vh; + overflow-y: scroll; } div#global-links ul { margin-left: 0; From 67122a9cb6be78b070a71941e74cbcc812633fa6 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sun, 6 Aug 2023 14:23:00 +0200 Subject: [PATCH 121/347] Let inferGenericTypes continue if a param is already bound (#22384) * Play with typeRel * Temp solution: Fixup call's param types * Test result type with two generic params * Asserts * Tiny cleanup * Skip sink * Ignore proc * Use changeType * Remove conversion * Remove last bits of conversion * Flag --------- Co-authored-by: SirOlaf <> --- compiler/semcall.nim | 9 ++++++--- tests/generics/treturn_inference.nim | 25 ++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index f0d0f648a67a..4af96a0eadda 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -564,7 +564,7 @@ proc getCallLineInfo(n: PNode): TLineInfo = proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = ## Helper proc to inherit bound generic parameters from expectedType into x. - ## Does nothing if 'inferGenericTypes' isn't in c.features + ## Does nothing if 'inferGenericTypes' isn't in c.features. if inferGenericTypes notin c.features: return if expectedType == nil or x.callee[0] == nil: return # required for inference @@ -578,7 +578,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = ## skips types and puts the skipped version on stack # It might make sense to skip here one by one. It's not part of the main # type reduction because the right side normally won't be skipped - const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass } + const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink } let x = a.skipTypes(toSkip) y = if a.kind notin toSkip: b @@ -603,7 +603,9 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = if t[i] == nil or u[i] == nil: return stackPut(t[i], u[i]) of tyGenericParam: - if x.bindings.idTableGet(t) != nil: return + let prebound = x.bindings.idTableGet(t).PType + if prebound != nil: + continue # Skip param, already bound # fully reduced generic param, bind it if t notin flatUnbound: @@ -611,6 +613,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = flatBound.add(u) else: discard + # update bindings for i in 0 ..< flatUnbound.len(): x.bindings.idTablePut(flatUnbound[i], flatBound[i]) diff --git a/tests/generics/treturn_inference.nim b/tests/generics/treturn_inference.nim index 05d38cef48b5..fa9b70f691f0 100644 --- a/tests/generics/treturn_inference.nim +++ b/tests/generics/treturn_inference.nim @@ -136,4 +136,27 @@ block: x: Table[int, float] let a = Foo(x: initTable()) - doAssert a.x is Table[int, float] \ No newline at end of file + doAssert a.x is Table[int, float] + +# partial binding +block: + type + ResultKind = enum + Ok, Error + + Result[T, E] = object + case kind: ResultKind + of Ok: + okVal: T + of Error: + errVal: E + + proc err[T, E](myParam: E): Result[T, E] = + Result[T, E](kind : Error, errVal : myParam) + + proc doStuff(): Result[int, string] = + err("Error") + + let res = doStuff() + doAssert res.kind == Error + doAssert res.errVal == "Error" \ No newline at end of file From 53586d1f32dfe4f2e859178a3e43a6614520763f Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sun, 6 Aug 2023 14:24:35 +0200 Subject: [PATCH 122/347] Fix some jsgen bugs (#22330) Fix `succ`, `pred` Fix `genRangeChck` for unsigned ints Fix typo in `dec` --- compiler/jsgen.nim | 53 +++++++++++++++++++++++++++++++------ compiler/semmagic.nim | 4 --- tests/int/tunsignedconv.nim | 36 ++++++++++++++++++------- 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a5f4d29b2775..f4d7d64563c4 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -676,8 +676,38 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = applyFormat("modInt64($1, $2)", "$1 % $2") else: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)") - of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)") - of mPred: applyFormat("subInt($1, $2)", "($1 - $2)") + of mSucc: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: + binaryUintExpr(p, n, r, "+") + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 + BigInt($2))") + else: binaryUintExpr(p, n, r, "+") + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + applyFormat("BigInt.asIntN(64, $1 + BigInt($2))") + else: binaryExpr(p, n, r, "addInt64", "addInt64($1, BigInt($2))") + else: + if optOverflowCheck notin p.options: applyFormat("$1 + $2") + else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)") + of mPred: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: + binaryUintExpr(p, n, r, "-") + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 - BigInt($2))") + else: binaryUintExpr(p, n, r, "-") + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + applyFormat("BigInt.asIntN(64, $1 - BigInt($2))") + else: binaryExpr(p, n, r, "subInt64", "subInt64($1, BigInt($2))") + else: + if optOverflowCheck notin p.options: applyFormat("$1 - $2") + else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)") of mAddF64: applyFormat("($1 + $2)", "($1 + $2)") of mSubF64: applyFormat("($1 - $2)", "($1 - $2)") of mMulF64: applyFormat("($1 * $2)", "($1 * $2)") @@ -2346,7 +2376,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of tyUInt64: if optJsBigInt64 in p.config.globalOptions: binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 - BigInt($2))", true) - else: binaryUintExpr(p, n, r, "+", true) + else: binaryUintExpr(p, n, r, "-", true) elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 - BigInt($2))", true) @@ -2564,12 +2594,19 @@ proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = gen(p, n[0], r) let src = skipTypes(n[0].typ, abstractVarRange) let dest = skipTypes(n.typ, abstractVarRange) - if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: - r.res = "Number($1)" % [r.res] - if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and - checkUnsignedConversions notin p.config.legacyFeatures): - discard "XXX maybe emit masking instructions here" + if optRangeCheck notin p.options: + return + elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures: + if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res] + else: + r.res = "BigInt.asUintN($1, BigInt($2))" % [$(dest.size * 8), r.res] + if not (dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions): + r.res = "Number($1)" % [r.res] else: + if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + # we do a range check anyway, so it's ok if the number gets rounded + r.res = "Number($1)" % [r.res] gen(p, n[1], a) gen(p, n[2], b) useMagic(p, "chckRange") diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 97a32077448c..4fba4eaf921b 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -656,10 +656,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if not checkIsolate(n[1]): localError(c.config, n.info, "expression cannot be isolated: " & $n[1]) result = n - of mPred: - if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}: - n[0].sym.magic = mSubU - result = n of mPrivateAccess: result = semPrivateAccess(c, n) of mArrToSeq: diff --git a/tests/int/tunsignedconv.nim b/tests/int/tunsignedconv.nim index c32f85b4dc41..6c73521d38ca 100644 --- a/tests/int/tunsignedconv.nim +++ b/tests/int/tunsignedconv.nim @@ -1,15 +1,19 @@ +discard """ + targets: "c cpp js" +""" + # Tests unsigned literals and implicit conversion between uints and ints -var h8:uint8 = 128 -var h16:uint16 = 32768 -var h32:uint32 = 2147483648'u32 -var h64:uint64 = 9223372036854775808'u64 -var foobar:uint64 = 9223372036854775813'u64 # Issue 728 +var h8: uint8 = 128 +var h16: uint16 = 32768 +var h32: uint32 = 2147483648'u32 +var h64: uint64 = 9223372036854775808'u64 +var foobar: uint64 = 9223372036854775813'u64 # Issue 728 -var v8:uint8 = 10 -var v16:uint16 = 10 -var v32:uint32 = 10 -var v64:uint64 = 10 +var v8: uint8 = 10 +var v16: uint16 = 10 +var v32: uint32 = 10 +var v64: uint64 = 10 # u8 + literal produces u8: var a8: uint8 = v8 + 10 @@ -95,3 +99,17 @@ template main() = static: main() main() + +block: + let a = uint64.high + let b = uint32.high + + doAssert a.uint64 == a + doAssert a.uint32 == uint32.high + doAssert a.uint16 == uint16.high + doAssert a.uint8 == uint8.high + + doAssert b.uint64 == b + doAssert b.uint32 == b + doAssert b.uint16 == uint16.high + doAssert b.uint8 == uint8.high From 93ced31353813c2f19c38a8c0af44737fa8d9f86 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 6 Aug 2023 20:26:21 +0800 Subject: [PATCH 123/347] use strictdefs for compiler (#22365) * wip; use strictdefs for compiler * checkpoint * complete the chores * more fixes * first phase cleanup * Update compiler/bitsets.nim * cleanup --- compiler/aliasanalysis.nim | 2 +- compiler/aliases.nim | 13 +- compiler/ast.nim | 16 +++ compiler/astalgo.nim | 5 + compiler/bitsets.nim | 1 + compiler/btrees.nim | 1 + compiler/ccgcalls.nim | 47 +++--- compiler/ccgexprs.nim | 221 +++++++++++++++-------------- compiler/ccgreset.nim | 2 +- compiler/ccgstmts.nim | 55 +++---- compiler/ccgtrav.nim | 6 +- compiler/ccgtypes.nim | 45 +++--- compiler/ccgutils.nim | 5 +- compiler/cgen.nim | 27 ++-- compiler/cgmeth.nim | 7 +- compiler/closureiters.nim | 13 +- compiler/commands.nim | 47 ++++-- compiler/concepts.nim | 7 + compiler/condsyms.nim | 2 + compiler/depends.nim | 1 + compiler/dfa.nim | 5 +- compiler/docgen.nim | 46 ++++-- compiler/docgen2.nim | 2 + compiler/enumtostr.nim | 1 + compiler/evalffi.nim | 23 ++- compiler/extccomp.nim | 25 ++-- compiler/filters.nim | 13 +- compiler/gorgeimpl.nim | 3 +- compiler/guards.nim | 148 +++++++++++++++++-- compiler/hlo.nim | 8 +- compiler/ic/cbackend.nim | 2 +- compiler/ic/dce.nim | 4 + compiler/ic/ic.nim | 9 +- compiler/ic/navigator.nim | 6 +- compiler/ic/packed_ast.nim | 1 + compiler/ic/rodfiles.nim | 6 +- compiler/importer.nim | 3 + compiler/injectdestructors.nim | 6 +- compiler/int128.nim | 6 +- compiler/isolation_check.nim | 7 +- compiler/jsgen.nim | 119 +++++++++------- compiler/lambdalifting.nim | 7 +- compiler/lexer.nim | 18 ++- compiler/liftdestructors.nim | 14 +- compiler/liftlocals.nim | 1 + compiler/lineinfos.nim | 2 +- compiler/linter.nim | 2 + compiler/llstream.nim | 3 + compiler/lookups.nim | 20 ++- compiler/magicsys.nim | 2 + compiler/main.nim | 4 +- compiler/modulegraphs.nim | 6 + compiler/msgs.nim | 6 +- compiler/nilcheck.nim | 18 +-- compiler/nim.cfg | 7 + compiler/nim.nim | 3 + compiler/nimblecmd.nim | 11 +- compiler/nimconf.nim | 6 +- compiler/nimsets.nim | 4 +- compiler/optimizer.nim | 2 +- compiler/options.nim | 5 +- compiler/packagehandling.nim | 1 + compiler/parampatterns.nim | 1 + compiler/parser.nim | 2 + compiler/patterns.nim | 40 +++++- compiler/pipelines.nim | 11 +- compiler/platform.nim | 2 + compiler/pragmas.nim | 4 + compiler/renderer.nim | 46 +++--- compiler/renderverbatim.nim | 1 + compiler/reorder.nim | 7 + compiler/rodutils.nim | 1 + compiler/ropes.nim | 6 +- compiler/sem.nim | 22 ++- compiler/semcall.nim | 16 ++- compiler/semexprs.nim | 21 ++- compiler/semfold.nim | 28 +++- compiler/semgnrc.nim | 3 +- compiler/seminst.nim | 5 +- compiler/semmagic.nim | 5 +- compiler/semobjconstr.nim | 27 ++-- compiler/semparallel.nim | 3 + compiler/sempass2.nim | 4 +- compiler/semstmts.nim | 31 +++- compiler/semtempl.nim | 1 + compiler/semtypes.nim | 39 +++-- compiler/semtypinst.nim | 3 + compiler/sighashes.nim | 8 +- compiler/sigmatch.nim | 25 +++- compiler/sizealignoffsetimpl.nim | 1 + compiler/sourcemap.nim | 14 +- compiler/spawn.nim | 4 +- compiler/suggest.nim | 33 +++-- compiler/syntaxes.nim | 10 +- compiler/transf.nim | 8 +- compiler/trees.nim | 21 ++- compiler/treetab.nim | 4 + compiler/typeallowed.nim | 2 + compiler/types.nim | 48 +++++-- compiler/typesrenderer.nim | 1 + compiler/varpartitions.nim | 13 +- compiler/vm.nim | 13 +- compiler/vmgen.nim | 20 ++- compiler/vmhooks.nim | 4 +- compiler/vmmarshal.nim | 27 +++- compiler/vmops.nim | 3 +- compiler/vmprofiler.nim | 4 +- lib/pure/collections/heapqueue.nim | 2 +- lib/pure/collections/sequtils.nim | 2 +- lib/pure/collections/setimpl.nim | 1 + lib/pure/collections/sets.nim | 2 +- lib/pure/collections/tableimpl.nim | 8 +- lib/pure/collections/tables.nim | 8 +- lib/std/packedsets.nim | 1 + 114 files changed, 1223 insertions(+), 501 deletions(-) diff --git a/compiler/aliasanalysis.nim b/compiler/aliasanalysis.nim index f14b8152438e..e24c6d8e260c 100644 --- a/compiler/aliasanalysis.nim +++ b/compiler/aliasanalysis.nim @@ -74,7 +74,7 @@ proc aliases*(obj, field: PNode): AliasKind = # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant template collectImportantNodes(result, n) = - var result: seq[PNode] + var result: seq[PNode] = @[] var n = n while true: case n.kind diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 4b50fdb28294..fa9824c41e47 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -114,6 +114,8 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = # use expensive type check: if isPartOf(a.sym.typ, b.sym.typ) != arNo: result = arMaybe + else: + result = arNo of nkBracketExpr: result = isPartOf(a[0], b[0]) if a.len >= 2 and b.len >= 2: @@ -149,7 +151,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = result = isPartOf(a[1], b[1]) of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: result = isPartOf(a[0], b[0]) - else: discard + else: result = arNo # Calls return a new location, so a default of ``arNo`` is fine. else: # go down recursively; this is quite demanding: @@ -165,6 +167,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = of DerefKinds: # a* !<| b[] iff + result = arNo if isPartOf(a.typ, b.typ) != arNo: result = isPartOf(a, b[0]) if result == arNo: result = arMaybe @@ -186,7 +189,9 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = if isPartOf(a.typ, b.typ) != arNo: result = isPartOf(a[0], b) if result == arNo: result = arMaybe - else: discard + else: + result = arNo + else: result = arNo of nkObjConstr: result = arNo for i in 1.. 0: result = isPartOf(a, b[0]) - else: discard + else: + result = arNo + else: result = arNo diff --git a/compiler/ast.nim b/compiler/ast.nim index 539b6e954760..eccf5a985250 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1036,6 +1036,8 @@ proc comment*(n: PNode): string = if nfHasComment in n.flags and not gconfig.useIc: # IC doesn't track comments, see `packed_ast`, so this could fail result = gconfig.comments[n.nodeId] + else: + result = "" proc `comment=`*(n: PNode, a: string) = let id = n.nodeId @@ -1222,6 +1224,7 @@ proc getDeclPragma*(n: PNode): PNode = case n.kind of routineDefs: if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos] + else: result = nil of nkTypeDef: #[ type F3*{.deprecated: "x3".} = int @@ -1241,6 +1244,8 @@ proc getDeclPragma*(n: PNode): PNode = ]# if n[0].kind == nkPragmaExpr: result = n[0][1] + else: + result = nil else: # support as needed for `nkIdentDefs` etc. result = nil @@ -1256,6 +1261,12 @@ proc extractPragma*(s: PSym): PNode = if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1: # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] result = s.ast[0][1] + else: + result = nil + else: + result = nil + else: + result = nil assert result == nil or result.kind == nkPragma proc skipPragmaExpr*(n: PNode): PNode = @@ -1602,6 +1613,7 @@ proc initStrTable*(x: var TStrTable) = newSeq(x.data, StartSize) proc newStrTable*: TStrTable = + result = default(TStrTable) initStrTable(result) proc initIdTable*(x: var TIdTable) = @@ -1609,6 +1621,7 @@ proc initIdTable*(x: var TIdTable) = newSeq(x.data, StartSize) proc newIdTable*: TIdTable = + result = default(TIdTable) initIdTable(result) proc resetIdTable*(x: var TIdTable) = @@ -1811,6 +1824,7 @@ proc hasNilSon*(n: PNode): bool = result = false proc containsNode*(n: PNode, kinds: TNodeKinds): bool = + result = false if n == nil: return case n.kind of nkEmpty..nkNilLit: result = n.kind in kinds @@ -2012,6 +2026,8 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool = if base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}: result = true + else: + result = false proc isInfixAs*(n: PNode): bool = return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as" diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 4e09fab02f63..d0aec085f565 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -197,6 +197,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym = result = nil proc sameIgnoreBacktickGensymInfo(a, b: string): bool = + result = false if a[0] != b[0]: return false var alen = a.len - 1 while alen > 0 and a[alen] != '`': dec(alen) @@ -230,6 +231,7 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), ## if c.instLines: actual.info else: templ.info) ## ``` + result = nil for i in 1.. 0: result.add ", " diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 67598f9cae22..756d93217c13 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -87,5 +87,6 @@ const populationCount: array[uint8, uint8] = block: arr proc bitSetCard*(x: TBitSet): BiggestInt = + result = 0 for it in x: result.inc int(populationCount[it]) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 92f07f6b096f..3b737b1bc924 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -38,6 +38,7 @@ template less(a, b): bool = cmp(a, b) < 0 template eq(a, b): bool = cmp(a, b) == 0 proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val = + result = default(Val) var x = b.root while x.isInternal: for j in 0.. # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; - var a, b, dest, tmpL, call: TLoc + var a, b, dest, tmpL, call: TLoc = default(TLoc) initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) let seqType = skipTypes(e[1].typ, {tyVar}) @@ -1369,7 +1377,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, n[1], a) specializeReset(p, a) when false: @@ -1384,7 +1392,7 @@ proc genDefault(p: BProc; n: PNode; d: var TLoc) = proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = var sizeExpr = sizeExpr let typ = a.t - var b: TLoc + var b: TLoc = default(TLoc) initLoc(b, locExpr, a.lode, OnHeap) let refType = typ.skipTypes(abstractInstOwned) assert refType.kind == tyRef @@ -1411,7 +1419,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = localError(p.module.config, a.lode.info, "the destructor that is turned into a finalizer needs " & "to have the 'nimcall' calling convention") - var f: TLoc + var f: TLoc = default(TLoc) initLocExpr(p, newSymNode(op), f) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) @@ -1437,11 +1445,11 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = genObjectInit(p, cpsStmts, bt, a, constructRefObj) proc genNew(p: BProc, e: PNode) = - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, e[1], a) # 'genNew' also handles 'unsafeNew': if e.len == 3: - var se: TLoc + var se: TLoc = default(TLoc) initLocExpr(p, e[2], se) rawGenNew(p, a, se.rdLoc, needsInit = true) else: @@ -1450,7 +1458,7 @@ proc genNew(p: BProc, e: PNode) = proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = let seqtype = skipTypes(dest.t, abstractVarRange) - var call: TLoc + var call: TLoc = default(TLoc) initLoc(call, locExpr, dest.lode, OnHeap) if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(p.module.g.graph, dest.t): @@ -1476,7 +1484,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = genAssignment(p, dest, call, {}) proc genNewSeq(p: BProc, e: PNode) = - var a, b: TLoc + var a, b: TLoc = default(TLoc) initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) if optSeqDestructors in p.config.globalOptions: @@ -1492,7 +1500,7 @@ proc genNewSeq(p: BProc, e: PNode) = proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, e[1], a) if optSeqDestructors in p.config.globalOptions: if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) @@ -1548,7 +1556,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or (isPartOf(d.lode, e) != arNo) - var tmp: TLoc + var tmp: TLoc = default(TLoc) var r: Rope if useTemp: getTemp(p, t, tmp) @@ -1567,7 +1575,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = let ty = getUniqueType(t) for i in 1..>3] &(1U<<((NU)($2)&7U)))!=0)") template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc + var a, b: TLoc = default(TLoc) assert(d.k == locNone) initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) @@ -2031,7 +2040,7 @@ template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = lineF(p, cpsStmts, frmt, [rdLoc(a), elem]) proc genInOp(p: BProc, e: PNode, d: var TLoc) = - var a, b, x, y: TLoc + var a, b, x, y: TLoc = default(TLoc) if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]): # a set constructor but not a constant set: # do not emit the set, but generate a bunch of comparisons; and if we do @@ -2081,7 +2090,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "&", "|", "& ~"] - var a, b, i: TLoc + var a, b, i: TLoc = default(TLoc) var setType = skipTypes(e[1].typ, abstractVar) var size = int(getSize(p.config, setType)) case size @@ -2118,7 +2127,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n") of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n") of mCard: - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, e[1], a) putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) of mLtSet, mLeSet: @@ -2133,7 +2142,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = linefmt(p, cpsStmts, lookupOpr[mLeSet], [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: - var a, b: TLoc + var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) initLocExpr(p, e[1], a) @@ -2161,7 +2170,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray} # we use whatever C gives us. Except if we have a value-type, we need to go # through its address: - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, e[1], a) let etyp = skipTypes(e.typ, abstractRange+{tyOwned}) let srcTyp = skipTypes(e[1].typ, abstractRange) @@ -2199,7 +2208,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = # 'cast' and some float type involved? --> use a union. inc(p.labels) var lbl = p.labels.rope - var tmp: TLoc + var tmp: TLoc = default(TLoc) tmp.r = "LOC$1.source" % [lbl] let destsize = getSize(p.config, destt) let srcsize = getSize(p.config, srct) @@ -2222,7 +2231,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = genSomeCast(p, e, d) proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc + var a: TLoc = default(TLoc) var dest = skipTypes(n.typ, abstractVar) initLocExpr(p, n[0], a) if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and @@ -2277,7 +2286,7 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = genSomeCast(p, e, d) proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, n[0], a) putIntoDest(p, d, n, ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]), @@ -2285,7 +2294,7 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = a.storage) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, n[0], a) putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), @@ -2293,7 +2302,7 @@ proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = gcUsage(p.config, n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = - var x: TLoc + var x: TLoc = default(TLoc) var a = e[1] var b = e[2] if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": @@ -2310,7 +2319,7 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if {optNaNCheck, optInfCheck} * p.options != {}: const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"] - var a, b: TLoc + var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) initLocExpr(p, e[1], a) @@ -2335,7 +2344,7 @@ proc skipAddr(n: PNode): PNode = result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n proc genWasMoved(p: BProc; n: PNode) = - var a: TLoc + var a: TLoc = default(TLoc) let n1 = n[1].skipAddr if p.withinBlockLeaveActions > 0 and notYetAlive(n1): discard @@ -2346,11 +2355,11 @@ proc genWasMoved(p: BProc; n: PNode) = # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) proc genMove(p: BProc; n: PNode; d: var TLoc) = - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, n[1].skipAddr, a) if n.len == 4: # generated by liftdestructors: - var src: TLoc + var src: TLoc = default(TLoc) initLocExpr(p, n[2], src) linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)]) genStmts(p, n[3]) @@ -2377,7 +2386,7 @@ proc genDestroy(p: BProc; n: PNode) = let t = arg.typ.skipTypes(abstractInst) case t.kind of tyString: - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, arg, a) if optThreads in p.config.globalOptions: linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & @@ -2388,7 +2397,7 @@ proc genDestroy(p: BProc; n: PNode) = " #dealloc($1.p);$n" & "}$n", [rdLoc(a)]) of tySequence: - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, arg, a) linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & @@ -2406,7 +2415,7 @@ proc genDispose(p: BProc; n: PNode) = when false: let elemType = n[1].typ.skipTypes(abstractVar).lastSon - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, n[1].skipAddr, a) if isFinal(elemType): @@ -2459,7 +2468,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: - var a, b: TLoc + var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) initLocExpr(p, e[1], a) @@ -2477,7 +2486,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optSeqDestructors in p.config.globalOptions: binaryStmtAddr(p, e, d, "nimAddCharV1") else: - var dest, b, call: TLoc + var dest, b, call: TLoc = default(TLoc) initLoc(call, locCall, e, OnHeap) initLocExpr(p, e[1], dest) initLocExpr(p, e[2], b) @@ -2515,7 +2524,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNew: genNew(p, e) of mNewFinalize: if optTinyRtti in p.config.globalOptions: - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, e[1], a) rawGenNew(p, a, "", needsInit = true) gcUsage(p.config, e) @@ -2541,6 +2550,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = elif e[1].kind == nkCheckedFieldExpr: dotExpr = e[1][0] else: + dotExpr = nil internalError(p.config, e.info, "unknown ast") let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) let tname = getTypeDesc(p.module, t, dkVar) @@ -2609,7 +2619,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = localError(p.config, e.info, "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on") - var a, b: TLoc + var a, b: TLoc = default(TLoc) let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] initLocExpr(p, x, a) initLocExpr(p, e[2], b) @@ -2635,7 +2645,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var - a, b, idx: TLoc + a, b, idx: TLoc = default(TLoc) if nfAllConst in e.flags: var elem = newRopeAppender() genSetNode(p, e, elem) @@ -2690,12 +2700,12 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = [rdLoc(d), aa, rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = - var rec: TLoc + var rec: TLoc = default(TLoc) if not handleConstExpr(p, n, d): let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized - var tmp: TLoc + var tmp: TLoc = default(TLoc) # bug #16331 let doesAlias = lhsDoesAlias(d.lode, n) let dest = if doesAlias: addr(tmp) else: addr(d) @@ -2734,7 +2744,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = p.module.s[cfsData].add data putIntoDest(p, d, n, tmp, OnStatic) else: - var tmp, a, b: TLoc + var tmp, a, b: TLoc = default(TLoc) initLocExpr(p, n[0], a) initLocExpr(p, n[1], b) if n[0].skipConv.kind == nkClosure: @@ -2751,7 +2761,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = putLocIntoDest(p, d, tmp) proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = - var arr: TLoc + var arr: TLoc = default(TLoc) if not handleConstExpr(p, n, d): if d.k == locNone: getTemp(p, n.typ, d) for i in 0..Sup" else: ".Sup") for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup") @@ -3049,7 +3059,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = let op = n[0] if n.typ.isNil: # discard the value: - var a: TLoc + var a: TLoc = default(TLoc) if op.kind == nkSym and op.sym.magic != mNone: genMagicExpr(p, n, a, op.sym.magic) else: @@ -3143,7 +3153,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = let ex = n[0] if ex.kind != nkEmpty: genLineDir(p, n) - var a: TLoc + var a: TLoc = default(TLoc) initLocExprSingleUse(p, ex, a) line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) @@ -3254,6 +3264,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = globalError(p.config, info, "cannot create null element for: " & $t.kind) proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = + result = 0 for i in 1 ..< obj.len: for j in 0 .. obj[i].len - 2: if obj[i][j].kind == nkRange: @@ -3464,11 +3475,11 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul if n[0].kind == nkNilLit: result.add "{NIM_NIL,NIM_NIL}" else: - var d: TLoc + var d: TLoc = default(TLoc) initLocExpr(p, n[0], d) result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] else: - var d: TLoc + var d: TLoc = default(TLoc) initLocExpr(p, n, d) result.add rdLoc(d) of tyArray, tyVarargs: @@ -3497,10 +3508,10 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: genStringLiteralV2Const(p.module, n, isConst, result) else: - var d: TLoc + var d: TLoc = default(TLoc) initLocExpr(p, n, d) result.add rdLoc(d) else: - var d: TLoc + var d: TLoc = default(TLoc) initLocExpr(p, n, d) result.add rdLoc(d) diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index 5e6456704dd2..f486f71fb722 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -57,7 +57,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = specializeResetT(p, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(p.config, typ[0]) - var i: TLoc + var i: TLoc = default(TLoc) getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, arraySize]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 3197389814c3..45104399a18c 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -50,6 +50,7 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = result = true proc inExceptBlockLen(p: BProc): int = + result = 0 for x in p.nestedTryStmts: if x.inExcept: result.inc @@ -71,7 +72,7 @@ template startBlock(p: BProc, start: FormatStr = "{$n", proc endBlock(p: BProc) proc genVarTuple(p: BProc, n: PNode) = - var tup, field: TLoc + var tup, field: TLoc = default(TLoc) if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") # if we have a something that's been captured, use the lowering instead: @@ -83,7 +84,7 @@ proc genVarTuple(p: BProc, n: PNode) = # check only the first son var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym) let hcrCond = if forHcr: getTempName(p.module) else: "" - var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] + var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] = @[] # determine if the tuple is constructed at top-level scope or inside of a block (if/while/block) let isGlobalInBlock = forHcr and p.blocks.len > 2 # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block) @@ -169,7 +170,7 @@ proc endBlock(p: BProc, blockEnd: Rope) = proc endBlock(p: BProc) = let topBlock = p.blocks.len - 1 let frameLen = p.blocks[topBlock].frameLen - var blockEnd: Rope + var blockEnd: Rope = "" if frameLen > 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) if p.blocks[topBlock].label.len != 0: @@ -244,7 +245,7 @@ proc genGotoState(p: BProc, n: PNode) = # switch (x.state) { # case 0: goto STATE0; # ... - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, n[0], a) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) p.flags.incl beforeRetNeeded @@ -263,7 +264,7 @@ proc genGotoState(p: BProc, n: PNode) = lineF(p, cpsStmts, "}$n", []) proc genBreakState(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc + var a: TLoc = default(TLoc) initLoc(d, locExpr, n, OnUnknown) if n[0].kind == nkClosure: @@ -357,7 +358,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # generate better code here: 'Foo f = x;' genLineDir(p, vn) var decl = localVarDecl(p, vn) - var tmp: TLoc + var tmp: TLoc = default(TLoc) if isCppCtorCall: genCppVarForCtor(p, v, vn, value, decl) line(p, cpsStmts, decl) @@ -441,7 +442,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = # { elsePart } # Lend: var - a: TLoc + a: TLoc = default(TLoc) lelse: TLabel if not isEmptyType(n.typ) and d.k == locNone: getTemp(p, n.typ, d) @@ -520,7 +521,7 @@ proc genComputedGoto(p: BProc; n: PNode) = # wrapped inside stmt lists by inject destructors won't be recognised let n = n.flattenStmts() var casePos = -1 - var arraySize: int + var arraySize: int = 0 for i in 0.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", @@ -1389,7 +1392,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = p.flags.incl noSafePoints genLineDir(p, t) cgsym(p.module, "Exception") - var safePoint: Rope + var safePoint: Rope = "" if not quirkyExceptions: safePoint = getTempName(p.module) linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint]) @@ -1492,7 +1495,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = of nkSym: var sym = it.sym if sym.kind in {skProc, skFunc, skIterator, skMethod}: - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, it, a) res.add($rdLoc(a)) elif sym.kind == skType: @@ -1505,7 +1508,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = res.add($getTypeDesc(p.module, it.typ)) else: discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs)) - var a: TLoc + var a: TLoc = default(TLoc) initLocExpr(p, it, a) res.add($a.rdLoc) @@ -1608,7 +1611,7 @@ when false: expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = - var a, tmp: TLoc + var a, tmp: TLoc = default(TLoc) var dotExpr = e[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] initLocExpr(p, e[0], a) @@ -1630,7 +1633,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = else: let le = e[0] let ri = e[1] - var a: TLoc + var a: TLoc = default(TLoc) discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar) initLoc(a, locNone, le, OnUnknown) a.flags.incl(lfEnforceDeref) @@ -1644,7 +1647,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = loadInto(p, le, ri, a) proc genStmts(p: BProc, t: PNode) = - var a: TLoc + var a: TLoc = default(TLoc) let isPush = p.config.hasHint(hintExtendedContext) if isPush: pushInfoContext(p.config, t.info) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 96f5869b0060..e4008bfc1eea 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -74,7 +74,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = genTraverseProc(c, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(c.p.config, typ[0]) - var i: TLoc + var i: TLoc = default(TLoc) getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) var oldCode = p.s(cpsStmts) freeze oldCode @@ -119,11 +119,11 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var p = c.p assert typ.kind == tySequence - var i: TLoc + var i: TLoc = default(TLoc) getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) var oldCode = p.s(cpsStmts) freeze oldCode - var a: TLoc + var a: TLoc = default(TLoc) a.r = accessor lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 2aa92c130e77..205031a918f9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -206,8 +206,12 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: if typ.n != nil: result = mapType(conf, lastSon typ, isParam) - else: doAssert(false, "mapType: " & $typ.kind) - else: doAssert(false, "mapType: " & $typ.kind) + else: + result = ctVoid + doAssert(false, "mapType: " & $typ.kind) + else: + result = ctVoid + doAssert(false, "mapType: " & $typ.kind) proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = @@ -322,7 +326,9 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope = of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ[0]) of tyStatic: if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) - else: internalError(m.config, "tyStatic for getSimpleTypeDesc") + else: + result = "" + internalError(m.config, "tyStatic for getSimpleTypeDesc") of tyGenericInst, tyAlias, tySink, tyOwned: result = getSimpleTypeDesc(m, lastSon typ) else: result = "" @@ -501,8 +507,8 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var rettype = getTypeDescAux(m, t[0], check, dkResult) else: rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t[0], check, dkResult)]) - var types, names, args: seq[string] - if not isCtor: + var types, names, args: seq[string] = @[] + if not isCtor: var this = t.n[1].sym fillParamName(m, this) fillLoc(this.loc, locParam, t.n[1], @@ -719,9 +725,9 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = genRecordFieldsAux(m, typ.n, typ, check, result) if typ.itemId in m.g.graph.memberProcsPerType: let procs = m.g.graph.memberProcsPerType[typ.itemId] - var isDefaultCtorGen, isCtorGen: bool + var isDefaultCtorGen, isCtorGen: bool = false for prc in procs: - var header: Rope + var header: Rope = "" if sfConstructor in prc.flags: isCtorGen = true if prc.typ.n.len == 1: @@ -741,7 +747,8 @@ proc fillObjectFields*(m: BModule; typ: PType) = proc mangleDynLibProc(sym: PSym): Rope proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, - check: var IntSet, hasField:var bool): Rope = + check: var IntSet, hasField:var bool): Rope = + result = "" if typ.kind == tyObject: if typ[0] == nil: if lacksMTypeField(typ): @@ -782,7 +789,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope, structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ) else: structOrUnion = structOrUnion(typ) - var baseType: string + var baseType: string = "" if typ[0] != nil: baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField) if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: @@ -873,6 +880,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym) let sig = hashType(origTyp, m.config) + result = "" # todo move `result = getTypePre(m, t, sig)` here ? defer: # defer is the simplest in this case if isImportedType(t) and not m.typeABICache.containsOrIncl(sig): addAbiCheck(m, t, result) @@ -953,7 +961,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes of tyProc: result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result - var rettype, desc: Rope + var rettype, desc: Rope = "" genProcParams(m, t, rettype, desc, check, true, true) if not isImportedType(t): if t.callConv != ccClosure: # procedure vars may need a closure! @@ -1030,7 +1038,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes while i < cppName.len: if cppName[i] == '\'': var chunkEnd = i-1 - var idx, stars: int + var idx, stars: int = 0 if scanCppGenericSlot(cppName, i, idx, stars): result.add cppName.substr(chunkStart, chunkEnd) chunkStart = i @@ -1110,7 +1118,7 @@ proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope = assert t.kind == tyProc var check = initIntSet() result = getTempName(m) - var rettype, desc: Rope + var rettype, desc: Rope = "" genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf) if not isImportedType(t): if t.callConv != ccClosure or kind != clFull: @@ -1141,7 +1149,7 @@ proc isNonReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride: var bool; isCtor: bool) = - var afterParams: string + var afterParams: string = "" if scanf(val, "$*($*)$s$*", name, params, afterParams): isFnConst = afterParams.find("const") > -1 isOverride = afterParams.find("override") > -1 @@ -1170,11 +1178,11 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = memberOp = "#->" var typDesc = getTypeDescWeak(m, typ, check, dkParam) let asPtrStr = rope(if asPtr: "_PTR" else: "") - var name, params, rettype, superCall: string - var isFnConst, isOverride: bool + var name, params, rettype, superCall: string = "" + var isFnConst, isOverride: bool = false parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isCtor) genMemberProcParams(m, prc, superCall, rettype, params, check, true, false) - var fnConst, override: string + var fnConst, override: string = "" if isCtor: name = typDesc if isFnConst: @@ -1203,7 +1211,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) var check = initIntSet() fillBackendName(m, prc) fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown) - var rettype, params: Rope + var rettype, params: Rope = "" genProcParams(m, prc.typ, rettype, params, check, true, false) # handle the 2 options for hotcodereloading codegen - function pointer # (instead of forward declaration) or header for function body with "_actual" postfix @@ -1443,7 +1451,7 @@ proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = genTypeInfoAux(m, typ, typ, name, info) var nodePtrs = getTempName(m) & "_" & $typ.n.len genTNimNodeArray(m, nodePtrs, rope(typ.n.len)) - var enumNames, specialCases: Rope + var enumNames, specialCases: Rope = "" var firstNimNode = m.typeNodes var hasHoles = false for i in 0.. 1: - var cond: PNode + var cond: PNode = nil for i in 0.. 0: # Ok, we are in a try, lets see which (if any) try's we break out from: for b in countdown(c.blocks.high, i): diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b25a82e4c460..4a0ae6fc9b27 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -170,6 +170,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int = proc prettyString(a: object): string = # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906 + result = "" for k, v in fieldPairs(a): result.add k & ": " & $v & "\n" @@ -215,12 +216,16 @@ proc whichType(d: PDoc; n: PNode): PSym = if n.kind == nkSym: if d.types.strTableContains(n.sym): result = n.sym + else: + result = nil else: + result = nil for i in 0.. 0: return @@ -484,7 +492,7 @@ proc externalDep(d: PDoc; module: PSym): string = proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; renderFlags: TRenderFlags = {}; procLink: string) = - var r: TSrcGen + var r: TSrcGen = TSrcGen() var literal = "" initTokRender(r, n, renderFlags) var kind = tkEof @@ -600,7 +608,9 @@ proc runAllExamples(d: PDoc) = rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string]) # removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove -proc quoted(a: string): string = result.addQuoted(a) +proc quoted(a: string): string = + result = "" + result.addQuoted(a) proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) = # xxx expose in compiler/lineinfos.nim @@ -726,7 +736,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre, let (rdoccmd, code) = prepareExample(d, n, topLevel) var msg = "Example:" if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd - var s: string + var s: string = "" dispA(d.conf, s, "\n

    $1

    \n", "\n\n\\textbf{$1}\n", [msg]) dest.add s @@ -942,7 +952,10 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string = if n[1].kind in {nkStrLit..nkTripleStrLit}: result = getConfigVar(d.conf, "doc.deprecationmsg") % [ "label", "Deprecated:", "message", xmltree.escape(n[1].strVal)] + else: + result = "" else: + result = "" doAssert false type DocFlags = enum @@ -950,6 +963,7 @@ type DocFlags = enum kForceExport proc genSeeSrc(d: PDoc, path: string, line: int): string = + result = "" let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") if docItemSeeSrc.len > 0: let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/') @@ -991,7 +1005,7 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = result.symKind = k.toHumanStr if k in routineKinds: var - paramTypes: seq[string] + paramTypes: seq[string] = @[] renderParamTypes(paramTypes, n[paramsPos], toNormalize=true) let paramNames = renderParamNames(n[paramsPos], toNormalize=true) # In some rare cases (system.typeof) parameter type is not set for default: @@ -1038,7 +1052,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx var result = "" var literal, plainName = "" var kind = tkEof - var comm: ItemPre + var comm: ItemPre = default(ItemPre) if n.kind in routineDefs: getAllRunnableExamples(d, n, comm) else: @@ -1150,7 +1164,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): var name = getNameEsc(d, nameNode) comm = genRecComment(d, n) - r: TSrcGen + r: TSrcGen = default(TSrcGen) renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing} if nonExports: renderFlags.incl renderNonExportedFields @@ -1283,6 +1297,8 @@ proc documentNewEffect(cache: IdentCache; n: PNode): PNode = let s = n[namePos].sym if tfReturnsNew in s.typ.flags: result = newIdentNode(getIdent(cache, "new"), n.info) + else: + result = nil proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode = let spec = effectSpec(x, effectType) @@ -1305,6 +1321,8 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id result = newTreeI(nkExprColonExpr, n.info, newIdentNode(getIdent(cache, $effectType), n.info), effects) + else: + result = nil proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = let s = n[namePos].sym @@ -1318,6 +1336,8 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName if effects.len > 0: result = newTreeI(nkExprColonExpr, n.info, newIdentNode(getIdent(cache, pragmaName), n.info), effects) + else: + result = nil proc documentRaises*(cache: IdentCache; n: PNode) = if n[namePos].kind != nkSym: return @@ -1391,7 +1411,7 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept" of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0]) of nkCallKinds: - var comm: ItemPre + var comm: ItemPre = default(ItemPre) getAllRunnableExamples(d, n, comm) if comm.len != 0: d.modDescPre.add(comm) else: discard @@ -1500,7 +1520,7 @@ proc finishGenerateDoc*(d: var PDoc) = overloadChoices.sort(cmp) var nameContent = "" for item in overloadChoices: - var itemDesc: string + var itemDesc: string = "" renderItemPre(d, item.descRst, itemDesc) nameContent.add( getConfigVar(d.conf, "doc.item") % ( @@ -1526,7 +1546,7 @@ proc finishGenerateDoc*(d: var PDoc) = for i, entry in d.jEntriesPre: if entry.rst != nil: let resolved = resolveSubs(d.sharedState, entry.rst) - var str: string + var str: string = "" renderRstToOut(d[], resolved, str) entry.json[entry.rstField] = %str d.jEntriesPre[i].rst = nil @@ -1641,7 +1661,7 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase): var overloadChoices = d.tocTable[kind][plainName] overloadChoices.sort(cmp) - var content: string + var content: string = "" for item in overloadChoices: content.add item.content d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [ @@ -1672,7 +1692,7 @@ proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): proc genOutFile(d: PDoc, groupedToc = false): string = var - code, content: string + code, content: string = "" title = "" var j = 0 var toc = "" @@ -1781,7 +1801,7 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = proc writeOutputJson*(d: PDoc, useWarning = false) = runAllExamples(d) - var modDesc: string + var modDesc: string = "" for desc in d.modDescFinal: modDesc &= desc let content = %*{"orig": d.filename, @@ -1793,7 +1813,7 @@ proc writeOutputJson*(d: PDoc, useWarning = false) = else: let dir = d.destFile.splitFile.dir createDir(dir) - var f: File + var f: File = default(File) if open(f, d.destFile, fmWrite): write(f, $content) close(f) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 90760341276c..7fb11a3bd765 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -39,10 +39,12 @@ template closeImpl(body: untyped) {.dirty.} = discard proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = + result = nil closeImpl: writeOutput(g.doc, useWarning, groupedToc) proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = + result = nil closeImpl: writeOutputJson(g.doc, useWarning) diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim index 4ae17235b307..838cd5f9716a 100644 --- a/compiler/enumtostr.nim +++ b/compiler/enumtostr.nim @@ -56,6 +56,7 @@ proc searchObjCaseImpl(obj: PNode; field: PSym): PNode = if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field: result = obj else: + result = nil for x in obj: result = searchObjCaseImpl(x, field) if result != nil: break diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 0112aebb914e..3f386f76ecce 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -37,9 +37,10 @@ else: var gExeHandle = loadLib() proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer = + result = nil if dll in cache: return cache[dll] - var libs: seq[string] + var libs: seq[string] = @[] libCandidates(dll, libs) for c in libs: result = loadLib(c) @@ -61,7 +62,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode = let lib = sym.annex if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}: globalError(conf, sym.info, "dynlib needs to be a string lit") - var theAddr: pointer + var theAddr: pointer = nil if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil: libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll # first try this exe itself: @@ -108,6 +109,7 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI of ccCDecl: result = DEFAULT_ABI else: + result = default(TABI) globalError(conf, info, "cannot map calling convention to FFI") template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[] @@ -132,6 +134,8 @@ proc packSize(conf: ConfigRef, v: PNode, typ: PType): int = result = sizeof(pointer) elif v.len != 0: result = v.len * packSize(conf, v[0], typ[1]) + else: + result = 0 else: result = getSize(conf, typ).int @@ -140,6 +144,7 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) proc getField(conf: ConfigRef, n: PNode; position: int): PSym = case n.kind of nkRecList: + result = nil for i in 0..= 5 else: @@ -644,7 +648,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1") let currentHash = footprint(conf, cfile) - var f: File + var f: File = default(File) if open(f, hashFile.string, fmRead): let oldHash = parseSecureHash(f.readLine()) close(f) @@ -779,6 +783,7 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body raise proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] = + result = @[] when defined(macosx): if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions: # if needed, add an option to skip or override location @@ -861,6 +866,7 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu result = conf.getNimcacheDir / RelativeFile(targetName) proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = + result = "" if conf.hasHint(hintCC): if optListCmd in conf.globalOptions or conf.verbosity > 1: result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd) @@ -883,15 +889,15 @@ proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) = proc callCCompiler*(conf: ConfigRef) = var - linkCmd: string + linkCmd: string = "" extraCmds: seq[string] if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: return # speed up that call if only compiling and no script shall be # generated #var c = cCompiler var script: Rope = "" - var cmds: TStringSeq - var prettyCmds: TStringSeq + var cmds: TStringSeq = default(TStringSeq) + var prettyCmds: TStringSeq = default(TStringSeq) let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for idx, it in conf.toCompile: @@ -1022,8 +1028,9 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool = + result = false if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true - var bcache: BuildCache + var bcache: BuildCache = default(BuildCache) try: bcache.fromJson(jsonFile.string.parseFile) except IOError, OSError, ValueError: stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string @@ -1039,7 +1046,7 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute if $secureHashFile(file) != hash: return true proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = - var bcache: BuildCache + var bcache: BuildCache = default(BuildCache) try: bcache.fromJson(jsonFile.string.parseFile) except ValueError, KeyError, JsonKindError: let e = getCurrentException() @@ -1052,7 +1059,8 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = globalError(conf, gCmdLineInfo, "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string]) - var cmds, prettyCmds: TStringSeq + var cmds: TStringSeq = default(TStringSeq) + var prettyCmds: TStringSeq= default(TStringSeq) let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for (name, cmd) in bcache.compile: cmds.add cmd @@ -1062,6 +1070,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting) proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope = + result = "" for it in list: result.addf("--file:r\"$1\"$N", [rope(it.cname.string)]) diff --git a/compiler/filters.nim b/compiler/filters.nim index 8151c0b93801..8d8af6b1c87c 100644 --- a/compiler/filters.nim +++ b/compiler/filters.nim @@ -29,23 +29,30 @@ proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode = return n[i] proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char = + var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind == nkCharLit: result = chr(int(x.intVal)) - else: invalidPragma(conf, n) + else: + result = default(char) + invalidPragma(conf, n) proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string = var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal - else: invalidPragma(conf, n) + else: + result = "" + invalidPragma(conf, n) proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool = var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false - else: invalidPragma(conf, n) + else: + result = false + invalidPragma(conf, n) proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream = var pattern = strArg(conf, call, "startswith", 1, "") diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index 558a6c9a3df1..fb0fafc98530 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -29,10 +29,11 @@ proc readOutput(p: Process): (string, int) = proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) = let workingDir = parentDir(toFullPath(conf, info)) + result = ("", 0) if cache.len > 0: let h = secureHash(cmd & "\t" & input & "\t" & cache) let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string - var f: File + var f: File = default(File) if optForceFullMake notin conf.globalOptions and open(f, filename): result = (f.readAll, 0) f.close diff --git a/compiler/guards.nim b/compiler/guards.nim index 15c6a64e36fd..1366a2382c43 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -51,6 +51,10 @@ proc isLet(n: PNode): bool = elif n.sym.kind == skParam and skipTypes(n.sym.typ, abstractInst).kind notin {tyVar}: result = true + else: + result = false + else: + result = false proc isVar(n: PNode): bool = n.kind == nkSym and n.sym.kind in {skResult, skVar} and @@ -136,6 +140,8 @@ proc neg(n: PNode; o: Operators): PNode = result = a elif b != nil: result = b + else: + result = nil else: # leave not (a == 4) as it is result = newNodeI(nkCall, n.info, 2) @@ -330,6 +336,8 @@ proc usefulFact(n: PNode; o: Operators): PNode = result = n elif n[1].getMagic in someLen or n[2].getMagic in someLen: result = n + else: + result = nil of someLe+someLt: if isLetLocation(n[1], true) or isLetLocation(n[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' @@ -337,12 +345,18 @@ proc usefulFact(n: PNode; o: Operators): PNode = elif n[1].getMagic in someLen or n[2].getMagic in someLen: # XXX Rethink this whole idea of 'usefulFact' for semparallel result = n + else: + result = nil of mIsNil: if isLetLocation(n[1], false) or isVar(n[1]): result = n + else: + result = nil of someIn: if isLetLocation(n[1], true): result = n + else: + result = nil of mAnd: let a = usefulFact(n[1], o) @@ -356,10 +370,14 @@ proc usefulFact(n: PNode; o: Operators): PNode = result = a elif b != nil: result = b + else: + result = nil of mNot: let a = usefulFact(n[1], o) if a != nil: result = a.neg(o) + else: + result = nil of mOr: # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything # with that knowledge... @@ -376,6 +394,8 @@ proc usefulFact(n: PNode; o: Operators): PNode = result[1] = a result[2] = b result = result.neg(o) + else: + result = nil elif n.kind == nkSym and n.sym.kind == skLet: # consider: # let a = 2 < x @@ -384,8 +404,12 @@ proc usefulFact(n: PNode; o: Operators): PNode = # We make can easily replace 'a' by '2 < x' here: if n.sym.astdef != nil: result = usefulFact(n.sym.astdef, o) + else: + result = nil elif n.kind == nkStmtListExpr: result = usefulFact(n.lastSon, o) + else: + result = nil type TModel* = object @@ -451,8 +475,9 @@ proc hasSubTree(n, x: PNode): bool = of nkEmpty..nkNilLit: result = n.sameTree(x) of nkFormalParams: - discard + result = false else: + result = false for i in 0.. unknown! if sameTree(fact[2], eq[val]): result = impYes elif valuesUnequal(fact[2], eq[val]): result = impNo + else: + result = impUnknown elif sameTree(fact[2], eq[loc]): if sameTree(fact[1], eq[val]): result = impYes elif valuesUnequal(fact[1], eq[val]): result = impNo + else: + result = impUnknown + else: + result = impUnknown of mInSet: # remember: mInSet is 'contains' so the set comes first! if sameTree(fact[2], eq[loc]) and isValue(eq[val]): if inSet(fact[1], eq[val]): result = impYes else: result = impNo - of mNot, mOr, mAnd: assert(false, "impliesEq") - else: discard + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesEq") + else: result = impUnknown proc leImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: @@ -512,13 +549,19 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication = var value = newIntNode(c.kind, firstOrd(nil, x.typ)) # don't iterate too often: if c.intVal - value.intVal < 1000: - var i, pos, neg: int + var i, pos, neg: int = 0 while value.intVal <= c.intVal: if inSet(aSet, value): inc pos else: inc neg inc i; inc value.intVal if pos == i: result = impYes elif neg == i: result = impNo + else: + result = impUnknown + else: + result = impUnknown + else: + result = impUnknown proc geImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: @@ -529,17 +572,23 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication = let max = lastOrd(nil, x.typ) # don't iterate too often: if max - getInt(value) < toInt128(1000): - var i, pos, neg: int + var i, pos, neg: int = 0 while value.intVal <= max: if inSet(aSet, value): inc pos else: inc neg inc i; inc value.intVal if pos == i: result = impYes elif neg == i: result = impNo + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown proc compareSets(a, b: PNode): TImplication = if equalSets(nil, a, b): result = impYes elif intersectSets(nil, a, b).len == 0: result = impNo + else: result = impUnknown proc impliesIn(fact, loc, aSet: PNode): TImplication = case fact[0].sym.magic @@ -550,22 +599,32 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication = elif sameTree(fact[2], loc): if inSet(aSet, fact[1]): result = impYes else: result = impNo + else: + result = impUnknown of mInSet: if sameTree(fact[2], loc): result = compareSets(fact[1], aSet) + else: + result = impUnknown of someLe: if sameTree(fact[1], loc): result = leImpliesIn(fact[1], fact[2], aSet) elif sameTree(fact[2], loc): result = geImpliesIn(fact[2], fact[1], aSet) + else: + result = impUnknown of someLt: if sameTree(fact[1], loc): result = leImpliesIn(fact[1], fact[2].pred, aSet) elif sameTree(fact[2], loc): # 4 < x --> 3 <= x result = geImpliesIn(fact[2], fact[1].pred, aSet) - of mNot, mOr, mAnd: assert(false, "impliesIn") - else: discard + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesIn") + else: result = impUnknown proc valueIsNil(n: PNode): TImplication = if n.kind == nkNilLit: impYes @@ -577,13 +636,19 @@ proc impliesIsNil(fact, eq: PNode): TImplication = of mIsNil: if sameTree(fact[1], eq[1]): result = impYes + else: + result = impUnknown of someEq: if sameTree(fact[1], eq[1]): result = valueIsNil(fact[2].skipConv) elif sameTree(fact[2], eq[1]): result = valueIsNil(fact[1].skipConv) - of mNot, mOr, mAnd: assert(false, "impliesIsNil") - else: discard + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesIsNil") + else: result = impUnknown proc impliesGe(fact, x, c: PNode): TImplication = assert isLocation(x) @@ -594,32 +659,57 @@ proc impliesGe(fact, x, c: PNode): TImplication = # fact: x = 4; question x >= 56? --> true iff 4 >= 56 if leValue(c, fact[2]): result = impYes else: result = impNo + else: + result = impUnknown elif sameTree(fact[2], x): if isValue(fact[1]) and isValue(c): if leValue(c, fact[1]): result = impYes else: result = impNo + else: + result = impUnknown + else: + result = impUnknown of someLt: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x < 4; question N <= x? --> false iff N <= 4 if leValue(fact[2], c): result = impNo + else: result = impUnknown # fact: x < 4; question 2 <= x? --> we don't know + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3 if isValue(fact[1]) and isValue(c): if leValue(c.pred, fact[1]): result = impYes + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown of someLe: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x <= 4; question x >= 56? --> false iff 4 <= 56 if leValue(fact[2], c): result = impNo # fact: x <= 4; question x >= 2? --> we don't know + else: + result = impUnknown + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3 if isValue(fact[1]) and isValue(c): if leValue(c, fact[1]): result = impYes - of mNot, mOr, mAnd: assert(false, "impliesGe") - else: discard + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesGe") + else: result = impUnknown proc impliesLe(fact, x, c: PNode): TImplication = if not isLocation(x): @@ -634,35 +724,59 @@ proc impliesLe(fact, x, c: PNode): TImplication = # fact: x = 4; question x <= 56? --> true iff 4 <= 56 if leValue(fact[2], c): result = impYes else: result = impNo + else: + result = impUnknown elif sameTree(fact[2], x): if isValue(fact[1]) and isValue(c): if leValue(fact[1], c): result = impYes else: result = impNo + else: + result = impUnknown + else: + result = impUnknown of someLt: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x < 4; question x <= N? --> true iff N-1 <= 4 if leValue(fact[2], c.pred): result = impYes + else: + result = impUnknown # fact: x < 4; question x <= 2? --> we don't know + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3 if isValue(fact[1]) and isValue(c): if leValue(c, fact[1]): result = impNo - + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown of someLe: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x <= 4; question x <= 56? --> true iff 4 <= 56 if leValue(fact[2], c): result = impYes + else: result = impUnknown # fact: x <= 4; question x <= 2? --> we don't know + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3 if isValue(fact[1]) and isValue(c): if leValue(c, fact[1].pred): result = impNo + else:result = impUnknown + else: + result = impUnknown + else: + result = impUnknown - of mNot, mOr, mAnd: assert(false, "impliesLe") - else: discard + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesLe") + else: result = impUnknown proc impliesLt(fact, x, c: PNode): TImplication = # x < 3 same as x <= 2: @@ -674,6 +788,8 @@ proc impliesLt(fact, x, c: PNode): TImplication = let q = x.pred if q != x: result = impliesLe(fact, q, c) + else: + result = impUnknown proc `~`(x: TImplication): TImplication = case x @@ -725,6 +841,7 @@ proc factImplies(fact, prop: PNode): TImplication = proc doesImply*(facts: TModel, prop: PNode): TImplication = assert prop.kind in nkCallKinds + result = impUnknown for f in facts.s: # facts can be invalidated, in which case they are 'nil': if not f.isNil: @@ -900,6 +1017,7 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode = proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = # now check for inferrable facts: a <= b and b <= c implies a <= c + result = impUnknown for i in 0..m.s.high: let fact = m.s[i] if fact != nil and fact.getMagic in someLe: @@ -981,7 +1099,7 @@ proc addFactLt*(m: var TModel; a, b: PNode) = proc settype(n: PNode): PType = result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner) - var idgen: IdGenerator + var idgen: IdGenerator = nil addSonSkipIntLit(result, n.typ, idgen) proc buildOf(it, loc: PNode; o: Operators): PNode = diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 2e1652f09d21..744fddcc0f63 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -20,9 +20,11 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode = # we need to ensure that the resulting AST is semchecked. However, it's # awful to semcheck before macro invocation, so we don't and treat # templates and macros as immediate in this context. - var rule: string - if c.config.hasHint(hintPattern): - rule = renderTree(n, {renderNoComments}) + var rule: string = + if c.config.hasHint(hintPattern): + renderTree(n, {renderNoComments}) + else: + "" let s = n[0].sym case s.kind of skMacro: diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 21f69e48520a..a1922c812d32 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -101,7 +101,7 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool var f2 = rodfiles.open(asymFile.string) f2.loadHeader() f2.loadSection aliveSymsSection - var oldData: seq[int32] + var oldData: seq[int32] = @[] f2.loadSeq(oldData) f2.close if f2.err == ok and oldData == s: diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index bc61a38dec47..ce64221010a4 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -40,10 +40,14 @@ proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): boo if ({sfExportc, sfCompilerProc} * flags != {}) or (symPtr.kind == skMethod): result = true + else: + result = false # XXX: This used to be a condition to: # (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or if sfCompilerProc in flags: c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId) + else: + result = false template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index a72db57c5f12..c2f3f793c376 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -813,6 +813,7 @@ proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; tree: PackedTree; n: NodePos): PNode = + result = nil var i = 0 for n0 in sonsReadonly(tree, n): if i == bodyPos: @@ -1147,6 +1148,8 @@ proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache; if it.i < it.values.len: result = loadSym(it.decoder, g, int(module), it.values[it.i]) inc it.i + else: + result = nil proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex, importHidden: bool): PSym = @@ -1164,11 +1167,15 @@ proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; if it.i < it.values.len: result = loadSym(it.decoder, g, int(module), it.values[it.i]) inc it.i + else: + result = nil proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym = if it.i < it.values.len: result = loadSym(it.decoder, g, it.module, it.values[it.i]) inc it.i + else: + result = nil iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex; @@ -1201,7 +1208,7 @@ proc searchForCompilerproc*(m: LoadedModule; name: string): int32 = # ------------------------- .rod file viewer --------------------------------- proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = - var m: PackedModule + var m: PackedModule = PackedModule() let err = loadRodFile(rodfile, m, config, ignoreConfig=true) if err != ok: config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index cbba591c5a8f..ab49b3b7a18d 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -34,7 +34,11 @@ proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool = if current.file == trackPos.file and current.line == trackPos.line: let col = trackPos.col if col >= current.col and col < current.col+tokenLen: - return true + result = true + else: + result = false + else: + result = false proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool = result = s.name != LitId(0) and diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 0bf5cd4c3110..8eafa5e968ae 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -305,6 +305,7 @@ proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) = result = (NodePos a, NodePos b, NodePos c) proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos = + result = default(NodePos) if tree.nodes[n.int].kind > nkNilLit: var count = 0 for child in sonsReadonly(tree, n): diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index e492624d043b..41e85084f156 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -215,7 +215,7 @@ proc storeHeader*(f: var RodFile) = proc loadHeader*(f: var RodFile) = ## Loads the header which is described by `cookie`. if f.err != ok: return - var thisCookie: array[cookie.len, byte] + var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte]) if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len: setError f, ioFailure elif thisCookie != cookie: @@ -231,13 +231,14 @@ proc storeSection*(f: var RodFile; s: RodSection) = proc loadSection*(f: var RodFile; expected: RodSection) = ## read the bytes value of s, sets and error if the section is incorrect. if f.err != ok: return - var s: RodSection + var s: RodSection = default(RodSection) loadPrim(f, s) if expected != s and f.err == ok: setError f, wrongSection proc create*(filename: string): RodFile = ## create the file and open it for writing + result = default(RodFile) if not open(result.f, filename, fmWrite): setError result, cannotOpen @@ -245,5 +246,6 @@ proc close*(f: var RodFile) = close(f.f) proc open*(filename: string): RodFile = ## open the file for reading + result = default(RodFile) if not open(result.f, filename, fmRead): setError result, cannotOpen diff --git a/compiler/importer.nim b/compiler/importer.nim index 54489ada4e9d..f5eb5329d919 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -113,6 +113,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) = proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) = template bail = globalError(c.config, n.info, "invalid pragma") + result = (nil, @[]) if n.kind == nkPragmaExpr: if n.len == 2 and n[1].kind == nkPragma: result[0] = n[0] @@ -307,6 +308,8 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = suggestSym(c.graph, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) + else: + result = nil proc afterImport(c: PContext, m: PSym) = # fixes bug #17510, for re-exported symbols diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 4463d1d69488..aa6470d3493d 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -432,6 +432,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = proc isCapturedVar(n: PNode): bool = let root = getRoot(n) if root != nil: result = root.name.s[0] == ':' + else: result = false proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -733,7 +734,9 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, result[^1] = maybeVoid(n[^1], s) dec c.inUncheckedAssignSection, inUncheckedAssignSection - else: assert(false) + else: + result = nil + assert(false) proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty: @@ -1042,6 +1045,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing of nkGotoState, nkState, nkAsmStmt: result = n else: + result = nil internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind) proc sameLocation*(a, b: PNode): bool = diff --git a/compiler/int128.nim b/compiler/int128.nim index b0341eb37958..6968b1f8922f 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -171,6 +171,7 @@ proc addToHex*(result: var string; arg: Int128) = i -= 1 proc toHex*(arg: Int128): string = + result = "" result.addToHex(arg) proc inc*(a: var Int128, y: uint32 = 1) = @@ -330,8 +331,8 @@ proc `*`*(a: Int128, b: int32): Int128 = if b < 0: result = -result -proc `*=`*(a: var Int128, b: int32): Int128 = - result = result * b +proc `*=`(a: var Int128, b: int32) = + a = a * b proc makeInt128(high, low: uint64): Int128 = result.udata[0] = cast[uint32](low) @@ -360,6 +361,7 @@ proc `*=`*(a: var Int128, b: Int128) = import bitops proc fastLog2*(a: Int128): int = + result = 0 if a.udata[3] != 0: return 96 + fastLog2(a.udata[3]) if a.udata[2] != 0: diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 273bfb7f9f2b..5fd1b8d51b5c 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -21,6 +21,7 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = case n.kind of nkRecList: + result = false for i in 0.. 2: @@ -833,7 +837,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = if mapType(n[1].typ) != etyBaseIndex: arithAux(p, n, r, op) else: - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res] @@ -866,7 +870,7 @@ proc genLineDir(p: PProc, n: PNode) = p.previousFileName = currentFileName proc genWhileStmt(p: PProc, n: PNode) = - var cond: TCompRes + var cond: TCompRes = default(TCompRes) internalAssert p.config, isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) @@ -961,6 +965,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = elif it.kind == nkType: throwObj = it else: + throwObj = nil internalError(p.config, n.info, "genTryStmt") if orExpr != "": orExpr.add("||") @@ -1001,7 +1006,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = proc genRaiseStmt(p: PProc, n: PNode) = if n[0].kind != nkEmpty: - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[0], a) let typ = skipTypes(n[0].typ, abstractPtrs) genLineDir(p, n) @@ -1015,7 +1020,7 @@ proc genRaiseStmt(p: PProc, n: PNode) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var - a, b, cond, stmt: TCompRes + a, b, cond, stmt: TCompRes = default(TCompRes) genLineDir(p, n) gen(p, n[0], cond) let typeKind = skipTypes(n[0].typ, abstractVar).kind @@ -1149,7 +1154,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = if false: discard else: - var r: TCompRes + var r = default(TCompRes) gen(p, it, r) if it.typ.kind == tyPointer: @@ -1165,13 +1170,13 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = p.body.add(r.rdLoc) else: - var r: TCompRes + var r: TCompRes = default(TCompRes) gen(p, it, r) p.body.add(r.rdLoc) p.body.add "\L" proc genIf(p: PProc, n: PNode, r: var TCompRes) = - var cond, stmt: TCompRes + var cond, stmt: TCompRes = default(TCompRes) var toClose = 0 if not isEmptyType(n.typ): r.kind = resVal @@ -1208,6 +1213,7 @@ proc generateHeader(p: PProc, typ: PType): Rope = result.add("_Idx") proc countJsParams(typ: PType): int = + result = 0 for i in 1..= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) @@ -2765,7 +2774,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = - var r: TCompRes + var r: TCompRes = default(TCompRes) gen(p, n, r) if r.res != "": lineF(p, "$#;$n", [r.res]) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ce36123b3a0e..ac4c160f9398 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -187,6 +187,8 @@ proc getEnvParam*(routine: PSym): PSym = if hidden.kind == nkSym and hidden.sym.name.s == paramName: result = hidden.sym assert sfFromGeneric in result.flags + else: + result = nil proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and @@ -199,6 +201,8 @@ proc illegalCapture(s: PSym): bool {.inline.} = proc isInnerProc(s: PSym): bool = if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone: result = s.skipGenericOwner.kind in routineKinds + else: + result = false proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would @@ -711,6 +715,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass; # direct dependency, so use the outer's env variable: result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info) else: + result = nil let available = getHiddenParam(d.graph, owner) let wanted = getHiddenParam(d.graph, s).typ # ugh: call through some other inner proc; @@ -936,7 +941,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): result = newNodeI(nkStmtList, body.info) # static binding? - var env: PSym + var env: PSym = nil let op = call[0] if op.kind == nkSym and op.sym.isIterator: # createClosure() diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 5962c8b9bb99..93a5f80406f9 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -148,9 +148,11 @@ proc isNimIdentifier*(s: string): bool = var i = 1 while i < sLen: if s[i] == '_': inc(i) - if i < sLen and s[i] notin SymChars: return + if i < sLen and s[i] notin SymChars: return false inc(i) result = true + else: + result = false proc `$`*(tok: Token): string = case tok.tokType @@ -537,8 +539,8 @@ proc getNumber(L: var Lexer, result: var Token) = of floatTypes: result.fNumber = parseFloat(result.literal) of tkUInt64Lit, tkUIntLit: - var iNumber: uint64 - var len: int + var iNumber: uint64 = uint64(0) + var len: int = 0 try: len = parseBiggestUInt(result.literal, iNumber) except ValueError: @@ -547,8 +549,8 @@ proc getNumber(L: var Lexer, result: var Token) = raise newException(ValueError, "invalid integer: " & result.literal) result.iNumber = cast[int64](iNumber) else: - var iNumber: int64 - var len: int + var iNumber: int64 = int64(0) + var len: int = 0 try: len = parseBiggestInt(result.literal, iNumber) except ValueError: @@ -1007,6 +1009,7 @@ proc getPrecedence*(tok: Token): int = else: return -10 proc newlineFollows*(L: Lexer): bool = + result = false var pos = L.bufpos while true: case L.buf[pos] @@ -1394,8 +1397,9 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache; config: ConfigRef): int = - var lex: Lexer - var tok: Token + result = 0 + var lex: Lexer = default(Lexer) + var tok: Token = default(Token) initToken(tok) openLexer(lex, fileIdx, inputstream, cache, config) var prevToken = tkEof diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 11d483abb3d9..760ee27b5d08 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -367,6 +367,8 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen) body.add newHookCall(c, op, x, y) result = true + else: + result = false elif tfHasAsgn in t.flags: var op: PSym if sameType(t, c.asgnForType): @@ -396,6 +398,8 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; assert op.ast[genericParamsPos].kind == nkEmpty body.add newHookCall(c, op, x, y) result = true + else: + result = false proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) = let t = orig.skipTypes(abstractInst - {tyDistinct}) @@ -435,6 +439,8 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = onUse(c.info, op) body.add destructorCall(c, op, x) result = true + else: + result = false #result = addDestructorCall(c, t, body, x) of attachedAsgn, attachedSink, attachedTrace: var op = getAttachedOp(c.g, t, c.kind) @@ -455,6 +461,8 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = onUse(c.info, op) body.add newDeepCopyCall(c, op, x, y) result = true + else: + result = false of attachedWasMoved: var op = getAttachedOp(c.g, t, attachedWasMoved) @@ -469,6 +477,8 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = onUse(c.info, op) body.add genWasMovedCall(c, op, x) result = true + else: + result = false of attachedDup: var op = getAttachedOp(c.g, t, attachedDup) @@ -483,6 +493,8 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = onUse(c.info, op) body.add newDupCall(c, op, x, y) result = true + else: + result = false proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info) @@ -1249,7 +1261,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf # bug #15122: We need to produce all prototypes before entering the # mind boggling recursion. Hacks like these imply we should rewrite # this module. - var generics: array[attachedWasMoved..attachedTrace, bool] + var generics: array[attachedWasMoved..attachedTrace, bool] = default(array[attachedWasMoved..attachedTrace, bool]) for k in attachedWasMoved..lastAttached: generics[k] = getAttachedOp(g, canon, k) != nil if not generics[k]: diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim index 7ca46ab1b8cb..58c6189d4098 100644 --- a/compiler/liftlocals.nim +++ b/compiler/liftlocals.nim @@ -49,6 +49,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) = liftLocals(it, i, c) proc lookupParam(params, dest: PNode): PSym = + result = nil if dest.kind != nkIdent: return nil for i in 1..= 0 and x[i] == ' ': dec(i) if i >= 0 and x[i] in s: result = true + else: + result = false const LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', @@ -93,6 +95,7 @@ proc continueLine(line: string, inTripleString: bool): bool {.inline.} = line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs)) proc countTriples(s: string): int = + result = 0 var i = 0 while i+2 < s.len: if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"': diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 17eedca924b7..8b9dd71fdc60 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -298,7 +298,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = var it: TTabIter var s = initTabIter(it, scope.symbols) var missingImpls = 0 - var unusedSyms: seq[tuple[sym: PSym, key: string]] + var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[] while s != nil: if sfForward in s.flags and s.kind notin {skType, skModule}: # too many 'implementation of X' errors are annoying @@ -458,7 +458,7 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) = for (sym, depth, isLocal) in allSyms(c): let depth = -depth - 1 let dist = editDistance(name0, sym.name.s.nimIdentNormalize) - var msg: string + var msg: string = "" msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s] list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym) @@ -488,6 +488,7 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS var err = "ambiguous identifier: '" & s.name.s & "'" var i = 0 var ignoredModules = 0 + result = nil for candidate in importedItems(c, s.name): if i == 0: err.add " -- use one of the following:\n" else: err.add "\n" @@ -586,6 +587,8 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = amb = candidates.len > 1 if amb and checkAmbiguity in flags: errorUseQualifier(c, n.info, candidates) + else: + result = nil if result == nil: let candidates = allPureEnumFields(c, ident) if candidates.len > 0: @@ -641,6 +644,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.marked = initIntSet() case n.kind of nkIdent, nkAccQuoted: + result = nil var ident = considerQuotedIdent(c, n) var scope = c.currentScope o.mode = oimNoQualifier @@ -664,6 +668,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = result = n.sym o.mode = oimDone of nkDotExpr: + result = nil o.mode = oimOtherModule o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule}) if o.m != nil and o.m.kind == skModule: @@ -693,7 +698,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.symChoiceIndex = 1 o.marked = initIntSet() incl(o.marked, result.id) - else: discard + else: result = nil when false: if result != nil and result.kind == skStub: loadStub(result) @@ -708,6 +713,7 @@ proc lastOverloadScope*(o: TOverloadIter): int = else: result = -1 proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym = + result = nil assert o.currentScope == nil var idx = o.importIdx+1 o.importIdx = c.imports.len # assume the other imported modules lack this symbol too @@ -720,6 +726,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym inc idx proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym = + result = nil assert o.currentScope == nil while o.importIdx < c.imports.len: result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph) @@ -782,6 +789,8 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = break if result != nil: incl o.marked, result.id + else: + result = nil of oimSymChoiceLocalLookup: if o.currentScope != nil: result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked) @@ -805,13 +814,16 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = if result == nil: inc o.importIdx result = symChoiceExtension(o, c, n) + else: + result = nil when false: if result != nil and result.kind == skStub: loadStub(result) proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; flags: TSymFlags = {}): PSym = - var o: TOverloadIter + result = nil + var o: TOverloadIter = default(TOverloadIter) var a = initOverloadIter(o, c, n) while a != nil: if a.kind in kinds and flags <= a.flags: diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index becde13e6d37..1b692f5d6257 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -30,6 +30,7 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule) proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = + result = nil let id = getIdent(g.cache, name) for r in systemModuleSyms(g, id): if r.magic == m: @@ -145,6 +146,7 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym = of tyProc: result = getSysMagic(g, info, "==", mEqProc) else: + result = nil globalError(g.config, info, "can't find magic equals operator for type kind " & $t.kind) diff --git a/compiler/main.nim b/compiler/main.nim index 364fba92e5bb..836f912bbcbb 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -56,7 +56,7 @@ proc writeCMakeDepsFile(conf: ConfigRef) = for it in conf.toCompile: cfiles.add(it.cname.string) let fileset = cfiles.toCountTable() # read old cfiles list - var fl: File + var fl: File = default(File) var prevset = initCountTable[string]() if open(fl, fname.string, fmRead): for line in fl.lines: prevset.inc(line) @@ -196,7 +196,7 @@ proc commandScan(cache: IdentCache, config: ConfigRef) = if stream != nil: var L: Lexer - tok: Token + tok: Token = default(Token) initToken(tok) openLexer(L, f, stream, cache, config) while true: diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 08cdbfd0db36..f9d0578b5ccc 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -368,6 +368,7 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) = setAttachedOp(g, module, dest, k, op) proc loadCompilerProc*(g: ModuleGraph; name: string): PSym = + result = nil if g.config.symbolFiles == disabledSf: return nil # slow, linear search, but the results are cached: @@ -500,6 +501,7 @@ proc resetAllModules*(g: ModuleGraph) = initModuleGraphFields(g) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = + result = nil if fileIdx.int32 >= 0: if isCachedModule(g, fileIdx.int32): result = g.packed[fileIdx.int32].module @@ -605,6 +607,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = proc needsCompilation*(g: ModuleGraph): bool = # every module that *depends* on this file is also dirty: + result = false for i in 0i32..= 0: result.add "\n" & indent & spaces(info.col) & '^' + else: + result = "" proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string = let title = case msg diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 5cc66f3ea43d..96e0967702cb 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -309,6 +309,7 @@ proc symbol(n: PNode): Symbol = # echo "symbol ", n, " ", n.kind, " ", result.int func `$`(map: NilMap): string = + result = "" var now = map var stack: seq[NilMap] = @[] while not now.isNil: @@ -416,7 +417,7 @@ proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) = if targetSetIndex != noSetIndex: var targetSet = map.sets[targetSetIndex] if targetSet.len > 1: - var other: ExprIndex + var other: ExprIndex = default(ExprIndex) for element in targetSet: if element.ExprIndex != targetIndex: @@ -561,7 +562,7 @@ proc derefWarning(n, ctx, map; kind: Nilability) = if n.info in ctx.warningLocations: return ctx.warningLocations.incl(n.info) - var a: seq[History] + var a: seq[History] = @[] if n.kind == nkSym: a = history(map, ctx.index(n)) var res = "" @@ -765,7 +766,7 @@ proc checkIf(n, ctx, map): Check = # the state of the conditions: negating conditions before the current one var layerHistory = newNilMap(mapIf) # the state after branch effects - var afterLayer: NilMap + var afterLayer: NilMap = nil # the result nilability for expressions var nilability = Safe @@ -862,9 +863,10 @@ proc checkInfix(n, ctx, map): Check = ## a or b : map is an union of a and b's ## a == b : use checkCondition ## else: no change, just check args + result = default(Check) if n[0].kind == nkSym: - var mapL: NilMap - var mapR: NilMap + var mapL: NilMap = nil + var mapR: NilMap = nil if n[0].sym.magic notin {mAnd, mEqRef}: mapL = checkCondition(n[1], ctx, map, false, false) mapR = checkCondition(n[2], ctx, map, false, false) @@ -947,7 +949,7 @@ proc checkCase(n, ctx, map): Check = let base = n[0] result.map = map.copyMap() result.nilability = Safe - var a: PNode + var a: PNode = nil for child in n: case child.kind: of nkOfBranch: @@ -1222,7 +1224,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = # TODO deeper nested elements? # A(field: B()) # # field: Safe -> - var elements: seq[(PNode, Nilability)] + var elements: seq[(PNode, Nilability)] = @[] for i, child in n: result = check(child, ctx, result.map) if i > 0: @@ -1333,7 +1335,7 @@ proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) = ctx.symbolIndices = {resultId: resultExprIndex}.toTable() var cache = newIdentCache() ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)]) - var emptySet: IntSet # set[ExprIndex] + var emptySet: IntSet = initIntSet() # set[ExprIndex] ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet]) for i, arg in s.typ.n.sons: if i > 0: diff --git a/compiler/nim.cfg b/compiler/nim.cfg index c32dba4d13a1..4c55a04cbc13 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -43,3 +43,10 @@ define:useStdoutAsStdmsg @if nimHasWarnBareExcept: warningAserror[BareExcept]:on @end + + +@if nimUseStrictDefs: + experimental:strictDefs + warningAsError[Uninit]:on + warningAsError[ProveInit]:on +@end diff --git a/compiler/nim.nim b/compiler/nim.nim index b28e8b20c6b2..d05f01c42717 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -91,6 +91,9 @@ proc getNimRunExe(conf: ConfigRef): string = if conf.isDefined("mingw"): if conf.isDefined("i386"): result = "wine" elif conf.isDefined("amd64"): result = "wine64" + else: result = "" + else: + result = "" proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 440d35fe5229..97a66f1cd99f 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -37,11 +37,16 @@ proc isSpecial(ver: Version): bool = proc isValidVersion(v: string): bool = if v.len > 0: - if v[0] in {'#'} + Digits: return true + if v[0] in {'#'} + Digits: + result = true + else: + result = false + else: + result = false proc `<`*(ver: Version, ver2: Version): bool = ## This is synced from Nimble's version module. - + result = false # Handling for special versions such as "#head" or "#branch". if ver.isSpecial or ver2.isSpecial: if ver2.isSpecial and ($ver2).normalize == "#head": @@ -145,7 +150,7 @@ proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) = conf.lazyPaths.insert(AbsoluteDir path, 0) proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) = - var packages: PackageInfo + var packages: PackageInfo = initTable[string, tuple[version, checksum: string]]() var pos = dir.len-1 if dir[pos] in {DirSep, AltSep}: inc(pos) for k,p in os.walkDir(dir): diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index fceedb2c4816..f7bae4b36838 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -214,7 +214,7 @@ proc parseAssignment(L: var Lexer, tok: var Token; proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; config: ConfigRef): bool = var - L: Lexer + L: Lexer = default(Lexer) tok: Token stream: PLLStream stream = llStreamOpen(filename, fmRead) @@ -228,6 +228,8 @@ proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; if condStack.len > 0: lexMessage(L, errGenerated, "expected @end") closeLexer(L) return true + else: + result = false proc getUserConfigPath*(filename: RelativeFile): AbsoluteFile = result = getConfigDir().AbsoluteDir / RelativeDir"nim" / filename @@ -250,7 +252,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: template runNimScriptIfExists(path: AbsoluteFile, isMain = false) = let p = path # eval once - var s: PLLStream + var s: PLLStream = nil if isMain and optWasNimscript in conf.globalOptions: if conf.projectIsStdin: s = stdin.llStreamOpen elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput) diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 49c80065ae21..59a542a8582a 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -62,7 +62,9 @@ proc someInSet*(s: PNode, a, b: PNode): bool = result = false proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet = - var first, j: Int128 + result = @[] + var first: Int128 = Zero + var j: Int128 = Zero first = firstOrd(conf, s.typ[0]) bitSetInit(result, int(getSize(conf, s.typ))) for i in 0.. 0: let b = a.split(".") assert b.len == 3, a @@ -657,7 +658,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = of "nimrawsetjmp": result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx} - else: discard + else: result = false template quitOrRaise*(conf: ConfigRef, msg = "") = # xxx in future work, consider whether to also intercept `msgQuit` calls @@ -883,6 +884,7 @@ const stdPrefix = "std/" proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile = + result = RelativeFile("") let f = $f if isTitle: for dir in stdlibDirs: @@ -918,6 +920,7 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) else: if m.startsWith(stdPrefix): + result = AbsoluteFile("") let stripped = m.substr(stdPrefix.len) for candidate in stdlibDirs: let path = (conf.libpath.string / candidate / stripped) diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 8cf209779eb1..30f407792ae9 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -17,6 +17,7 @@ iterator myParentDirs(p: string): string = proc getNimbleFile*(conf: ConfigRef; path: string): string = ## returns absolute path to nimble file, e.g.: /pathto/cligen.nimble + result = "" var parents = 0 block packageSearch: for d in myParentDirs(path): diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 534c3b5d1849..98f5099d688e 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -184,6 +184,7 @@ type arStrange # it is a strange beast like 'typedesc[var T]' proc exprRoot*(n: PNode; allowCalls = true): PSym = + result = nil var it = n while true: case it.kind diff --git a/compiler/parser.nim b/compiler/parser.nim index 7d12c2a7859c..c386df57bf27 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1177,6 +1177,7 @@ proc optPragmas(p: var Parser): PNode = proc parseDoBlock(p: var Parser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragma? colcom stmt + result = nil var params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) colcom(p, result) @@ -1430,6 +1431,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode = result = newNodeP(nkObjectTy, p) getTok(p) of tkConcept: + result = nil parMessage(p, "the 'concept' keyword is only valid in 'type' sections") of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc) of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index ff9a9efa347e..7b0d7e4fb43d 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -29,6 +29,8 @@ type proc getLazy(c: PPatternContext, sym: PSym): PNode = if c.mappingIsFull: result = c.mapping[sym.position] + else: + result = nil proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = if not c.mappingIsFull: @@ -65,14 +67,21 @@ proc sameTrees*(a, b: PNode): bool = for i in 0..= 2: for i in 1.. 1 var key = if keyDeep: it[0] else: it diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b9c3268c4ce4..2af8d83269ec 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -76,6 +76,8 @@ proc isKeyword*(i: PIdent): bool = if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): result = true + else: + result = false proc isExported(n: PNode): bool = ## Checks if an ident is exported. @@ -274,6 +276,7 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = + result = 0 if s.len == 0: return 0 var i = 0 let hi = s.len - 1 @@ -371,6 +374,7 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}: result = lastSon(result) + result = "" let typ = n.typ.skip if typ != nil and typ.kind in {tyBool, tyEnum}: if sfPure in typ.sym.flags: @@ -488,6 +492,7 @@ proc referencesUsing(n: PNode): bool = proc lsub(g: TSrcGen; n: PNode): int = # computes the length of a tree + result = 0 if isNil(n): return 0 if shouldRenderComment(g, n): return MaxLineLen + 1 case n.kind @@ -631,7 +636,7 @@ proc initContext(c: var TContext) = proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) = - var c: TContext + var c: TContext = default(TContext) initContext(c) gsub(g, n, c, fromStmtList = fromStmtList) @@ -762,7 +767,7 @@ proc gcond(g: var TSrcGen, n: PNode) = put(g, tkParRi, ")") proc gif(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) gcond(g, n[0][0]) initContext(c) putWithSpace(g, tkColon, ":") @@ -775,7 +780,7 @@ proc gif(g: var TSrcGen, n: PNode) = gsub(g, n[i], c) proc gwhile(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) putWithSpace(g, tkWhile, "while") gcond(g, n[0]) putWithSpace(g, tkColon, ":") @@ -786,7 +791,7 @@ proc gwhile(g: var TSrcGen, n: PNode) = gstmts(g, n[1], c) proc gpattern(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) put(g, tkCurlyLe, "{") initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): @@ -796,7 +801,7 @@ proc gpattern(g: var TSrcGen, n: PNode) = put(g, tkCurlyRi, "}") proc gpragmaBlock(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) gsub(g, n[0]) putWithSpace(g, tkColon, ":") initContext(c) @@ -806,7 +811,7 @@ proc gpragmaBlock(g: var TSrcGen, n: PNode) = gstmts(g, n[1], c) proc gtry(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) put(g, tkTry, "try") putWithSpace(g, tkColon, ":") initContext(c) @@ -817,7 +822,7 @@ proc gtry(g: var TSrcGen, n: PNode) = gsons(g, n, c, 1) proc gfor(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) putWithSpace(g, tkFor, "for") initContext(c) if longMode(g, n) or @@ -832,7 +837,7 @@ proc gfor(g: var TSrcGen, n: PNode) = gstmts(g, n[^1], c) proc gcase(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) initContext(c) if n.len == 0: return var last = if n[^1].kind == nkElse: -2 else: -1 @@ -853,7 +858,7 @@ proc genSymSuffix(result: var string, s: PSym) {.inline.} = result.addInt s.id proc gproc(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) if n[namePos].kind == nkSym: let s = n[namePos].sym var ret = renderDefinitionName(s) @@ -889,7 +894,7 @@ proc gproc(g: var TSrcGen, n: PNode) = dedent(g) proc gTypeClassTy(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) initContext(c) putWithSpace(g, tkConcept, "concept") gsons(g, n[0], c) # arglist @@ -909,7 +914,7 @@ proc gblock(g: var TSrcGen, n: PNode) = if n.len == 0: return - var c: TContext + var c: TContext = default(TContext) initContext(c) if n[0].kind != nkEmpty: @@ -930,7 +935,7 @@ proc gblock(g: var TSrcGen, n: PNode) = gstmts(g, n[1], c) proc gstaticStmt(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = default(TContext) putWithSpace(g, tkStatic, "static") putWithSpace(g, tkColon, ":") initContext(c) @@ -1005,6 +1010,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = case n.kind of nkClosedSymChoice, nkOpenSymChoice: if n.len > 0: result = bracketKind(g, n[0]) + else: result = bkNone of nkSym: result = case n.sym.name.s of "[]": bkBracket @@ -1013,6 +1019,8 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = of "{}=": bkCurlyAsgn else: bkNone else: result = bkNone + else: + result = bkNone proc skipHiddenNodes(n: PNode): PNode = result = n @@ -1085,11 +1093,13 @@ proc isCustomLit(n: PNode): bool = if n.len == 2 and n[0].kind == nkRStrLit: let ident = n[1].getPIdent result = ident != nil and ident.s.startsWith('\'') + else: + result = false proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = if isNil(n): return var - a: TContext + a: TContext = default(TContext) if shouldRenderComment(g, n): pushCom(g, n) case n.kind # atoms: of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n)) @@ -1437,6 +1447,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = if n.kind in {nkIdent, nkSym}: let tmp = n.getPIdent.s result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'} + else: + result = false var useSpace = false if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]: if not n[1].isAlpha: # handle `=destroy`, `'big' @@ -1787,12 +1799,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n, 0) put(g, tkParRi, ")") of nkGotoState: - var c: TContext + var c: TContext = default(TContext) initContext c putWithSpace g, tkSymbol, "goto" gsons(g, n, c) of nkState: - var c: TContext + var c: TContext = default(TContext) initContext c putWithSpace g, tkSymbol, "state" gsub(g, n[0], c) @@ -1817,7 +1829,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = if n == nil: return "" - var g: TSrcGen + var g: TSrcGen = default(TSrcGen) initSrcGen(g, renderFlags, newPartialConfigRef()) # do not indent the initial statement list so that # writeFile("file.nim", repr n) @@ -1835,7 +1847,7 @@ proc renderModule*(n: PNode, outfile: string, fid = FileIndex(-1); conf: ConfigRef = nil) = var - f: File + f: File = default(File) g: TSrcGen initSrcGen(g, renderFlags, conf) g.fid = fid diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index 792079b3f44e..00d546198aab 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -40,6 +40,7 @@ type LineData = object proc tripleStrLitStartsAtNextLine(conf: ConfigRef, n: PNode): bool = # enabling TLineInfo.offsetA,offsetB would probably make this easier + result = false const tripleQuote = "\"\"\"" let src = sourceLine(conf, n.info) let col = n.info.col diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 4ad3f12194ef..f43ddc2031da 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -115,6 +115,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev for i in 0.. 0: # we avoid running more diagnostic when inside a `compiles(expr)`, to # errors while running diagnostic (see test D20180828T234921), and @@ -405,8 +406,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds, flags: TExprFlags, errors: var CandidateErrors, errorsEnabled: bool): TCandidate = + result = default(TCandidate) var initialBinding: PNode - var alt: TCandidate + var alt: TCandidate = default(TCandidate) var f = n[0] if f.kind == nkBracketExpr: # fill in the bindings: @@ -569,8 +571,8 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = if expectedType == nil or x.callee[0] == nil: return # required for inference var - flatUnbound: seq[PType] - flatBound: seq[PType] + flatUnbound: seq[PType] = @[] + flatBound: seq[PType] = @[] # seq[(result type, expected type)] var typeStack = newSeq[(PType, PType)]() @@ -694,7 +696,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # this time enabling all the diagnostic output (this should fail again) result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) elif efNoUndeclared notin flags: + result = nil notFoundError(c, n, errors) + else: + result = nil proc explicitGenericInstError(c: PContext; n: PNode): PNode = localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n)) @@ -771,6 +776,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = # for borrowing the sym in the symbol table is returned, else nil. # New approach: generate fn(x, y, z) where x, y, z have the proper types # and use the overloading resolution mechanism: + result = nil var call = newNodeI(nkCall, fn.info) var hasDistinct = false call.add(newIdentNode(fn.name, fn.info)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b7fc7a9bd808..dca4ce6e0d98 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -655,7 +655,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp else: discard rawAddSon(result.typ, nil) # index type var - firstIndex, lastIndex: Int128 + firstIndex, lastIndex: Int128 = Zero indexType = getSysType(c.graph, n.info, tyInt) lastValidIndex = lastOrd(c.config, indexType) if n.len == 0: @@ -990,6 +990,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, proc resolveIndirectCall(c: PContext; n, nOrig: PNode; t: PType): TCandidate = + result = default(TCandidate) initCandidate(c, result, t) matches(c, n, nOrig, result) @@ -998,6 +999,8 @@ proc bracketedMacro(n: PNode): PSym = result = n[0].sym if result.kind notin {skMacro, skTemplate}: result = nil + else: + result = nil proc setGenericParams(c: PContext, n: PNode) = for i in 1.. 0: # don't interpret () as type isTupleType = tupexp[0].typ.kind == tyTypeDesc # check if either everything or nothing is tyTypeDesc @@ -2837,6 +2843,7 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp result = tupexp proc shouldBeBracketExpr(n: PNode): bool = + result = false assert n.kind in nkCallKinds let a = n[0] if a.kind in nkCallKinds: @@ -2854,6 +2861,8 @@ proc asBracketExpr(c: PContext; n: PNode): PNode = if n.kind in {nkIdent, nkAccQuoted}: let s = qualifiedLookUp(c, n, {}) result = s != nil and isGenericRoutineStrict(s) + else: + result = false assert n.kind in nkCallKinds if n.len > 1 and isGeneric(c, n[1]): @@ -2983,7 +2992,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - var s: PSym + var s: PSym = nil if expectedType != nil and ( let expected = expectedType.skipTypes(abstractRange-{tyDistinct}); expected.kind == tyEnum): diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 07c3b3ae4e8c..a60bfee2a9be 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -65,24 +65,34 @@ proc foldAdd(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode let res = a + b if checkInRange(g.config, n, res): result = newIntNodeT(res, n, idgen, g) + else: + result = nil proc foldSub(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = let res = a - b if checkInRange(g.config, n, res): result = newIntNodeT(res, n, idgen, g) + else: + result = nil proc foldUnarySub(a: Int128, n: PNode; idgen: IdGenerator, g: ModuleGraph): PNode = if a != firstOrd(g.config, n.typ): result = newIntNodeT(-a, n, idgen, g) + else: + result = nil proc foldAbs(a: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = if a != firstOrd(g.config, n.typ): result = newIntNodeT(abs(a), n, idgen, g) + else: + result = nil proc foldMul(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = let res = a * b if checkInRange(g.config, n, res): return newIntNodeT(res, n, idgen, g) + else: + result = nil proc ordinalValToString*(a: PNode; g: ModuleGraph): string = # because $ has the param ordinal[T], `a` is not necessarily an enum, but an @@ -94,6 +104,7 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string = of tyChar: result = $chr(toInt64(x) and 0xff) of tyEnum: + result = "" var n = t.n for i in 0.. 2: b = getConstExpr(m, n[2], idgen, g) @@ -396,7 +407,9 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`? result = a result.typ = n.typ - else: doAssert false, $srcTyp.kind + else: + result = nil + doAssert false, $srcTyp.kind of tyInt..tyInt64, tyUInt..tyUInt64: case srcTyp.kind of tyFloat..tyFloat64: @@ -420,7 +433,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P result = a result.typ = n.typ of tyOpenArray, tyVarargs, tyProc, tyPointer: - discard + result = nil else: result = a result.typ = n.typ @@ -447,21 +460,25 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo result = x.sons[idx] if result.kind == nkExprColonExpr: result = result[1] else: + result = nil localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) of nkBracket: idx -= toInt64(firstOrd(g.config, x.typ)) if idx >= 0 and idx < x.len: result = x[int(idx)] - else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) + else: + result = nil + localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) of nkStrLit..nkTripleStrLit: result = newNodeIT(nkCharLit, x.info, n.typ) if idx >= 0 and idx < x.strVal.len: result.intVal = ord(x.strVal[int(idx)]) else: localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n) - else: discard + else: result = nil proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = # a real field access; proc calls have already been transformed + result = nil if n[1].kind != nkSym: return nil var x = getConstExpr(m, n[0], idgen, g) if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return @@ -496,6 +513,7 @@ proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode = result.typ = s.typ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = + result = nil var name = s.name.s let prag = extractPragma(s) if prag != nil: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 70cb64b5168c..43b8d4bac451 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -59,6 +59,7 @@ template isMixedIn(sym): bool = proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, ctx: var GenericCtx; flags: TSemGenericFlags, fromDotExpr=false): PNode = + result = nil semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) incl(s.flags, sfUsed) template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) = @@ -439,7 +440,7 @@ proc semGenericStmt(c: PContext, n: PNode, if n[0].kind != nkEmpty: n[0] = semGenericStmt(c, n[0], flags+{withinTypeDesc}, ctx) for i in 1..= 0 and c.locals[s].stride != nil: result = c.locals[s].stride.intVal + else: + result = 0 else: + result = 0 for i in 0..= 1: p[0] else: p @@ -2251,7 +2266,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = of nkIdentDefs: var def = a[^1] let constraint = a[^2] - var typ: PType + var typ: PType = nil if constraint.kind != nkEmpty: typ = semTypeNode(c, constraint, nil) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 7c9bf9039d1e..19aa8be29104 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -36,6 +36,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = localError(info, errInheritanceOnlyWithNonFinalObjects) proc searchInstTypes*(g: ModuleGraph; key: PType): PType = + result = nil let genericTyp = key[0] if not (genericTyp.kind == tyGenericBody and genericTyp.sym != nil): return @@ -100,6 +101,7 @@ proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = initIdTable(result.topLayer) proc lookup(typeMap: LayeredIdTable, key: PType): PType = + result = nil var tm = typeMap while tm != nil: result = PType(idTableGet(tm.topLayer, key)) @@ -683,6 +685,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = + result = default(TReplTypeVars) initIdTable(result.symMap) initIdTable(result.localCache) result.typeMap = typeMap diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 2d91fb2a01fd..9940d0c68e9a 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -159,7 +159,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi else: c.hashSym(t.sym) - var symWithFlags: PSym + var symWithFlags: PSym = nil template hasFlag(sym): bool = let ret = {sfAnon, sfGenSym} * sym.flags != {} if ret: symWithFlags = sym @@ -260,6 +260,7 @@ when defined(debugSigHashes): # (select hash from sighashes group by hash having count(*) > 1) order by hash; proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash = + result = default(SigHash) var c: MD5Context md5Init c hashType c, t, flags+{CoOwnerSig}, conf @@ -269,6 +270,7 @@ proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): typeToString(t), $result) proc hashProc(s: PSym; conf: ConfigRef): SigHash = + result = default(SigHash) var c: MD5Context md5Init c hashType c, s.typ, {CoProc}, conf @@ -289,6 +291,7 @@ proc hashProc(s: PSym; conf: ConfigRef): SigHash = md5Final c, result.MD5Digest proc hashNonProc*(s: PSym): SigHash = + result = default(SigHash) var c: MD5Context md5Init c hashSym(c, s) @@ -305,6 +308,7 @@ proc hashNonProc*(s: PSym): SigHash = md5Final c, result.MD5Digest proc hashOwner*(s: PSym): SigHash = + result = default(SigHash) var c: MD5Context md5Init c var m = s @@ -374,7 +378,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = ## compute unique digest of the proc/func/method symbols ## recursing into invoked symbols as well assert(sym.kind in skProcKinds, $sym.kind) - + result = default(SigHash) graph.symBodyHashes.withValue(sym.id, value): return value[] diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 12cc1fcb127c..9bf47df70079 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -170,9 +170,11 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = + result = default(TCandidate) initCandidate(ctx, result, callee, binding, calleeScope) proc newCandidate*(ctx: PContext, callee: PType): TCandidate = + result = default(TCandidate) initCandidate(ctx, result, callee) proc copyCandidate(a: var TCandidate, b: TCandidate) = @@ -214,6 +216,7 @@ proc sumGeneric(t: PType): int = # count the "genericness" so that Foo[Foo[T]] has the value 3 # and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more # specific than Foo[T]. + result = 0 var t = t var isvar = 0 while true: @@ -344,6 +347,7 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p result.add argTypeToString(arg, prefer) proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string = + result = "" describeArgImpl(c, n, i, startIdx, prefer) proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = @@ -440,6 +444,8 @@ proc isConvertibleToRange(c: PContext, f, a: PType): bool = # `isIntLit` is correct and should be used above as well, see PR: # https://github.com/nim-lang/Nim/pull/11197 result = isIntLit(a) or a.kind in {tyFloat..tyFloat128} + else: + result = false proc handleFloatRange(f, a: PType): TTypeRelation = if a.kind == f.kind: @@ -506,6 +512,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = else: break if r.kind == tyObject and ptrs <= 1: result = r + else: result = nil proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool = assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} @@ -528,6 +535,8 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin genericParamPut(c, last, fGenericOrigin) d = depth result = true + else: + result = false proc minRel(a, b: TTypeRelation): TTypeRelation = if a <= b: result = a @@ -609,6 +618,8 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = if reverseRel >= isGeneric: result = isInferred #inc c.genericMatches + else: + result = isNone else: # Note that this typeRel call will save f's resolved type into c.bindings # if f is metatype. @@ -656,7 +667,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyNil: result = f.allowsNil - else: discard + else: result = isNone proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = template checkRange[T](a0, a1, f0, f1: T): TTypeRelation = @@ -701,14 +712,14 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = typeClass[0][0] = prevCandidateType closeScope(c) - var typeParams: seq[(PSym, PType)] + var typeParams: seq[(PSym, PType)] = @[] if ff.kind == tyUserTypeClassInst: for i in 1..<(ff.len - 1): var typeParamName = ff.base[i-1].sym.name typ = ff[i] - param: PSym + param: PSym = nil alreadyBound = PType(idTableGet(m.bindings, typ)) if alreadyBound != nil: typ = alreadyBound @@ -748,8 +759,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = addDecl(c, param) var - oldWriteHook: typeof(m.c.config.writelnHook) - diagnostics: seq[string] + oldWriteHook = default typeof(m.c.config.writelnHook) + diagnostics: seq[string] = @[] errorPrefix: string flags: TExprFlags = {} collectDiagnostics = m.diagnosticsEnabled or @@ -923,6 +934,7 @@ proc inferStaticsInRange(c: var TCandidate, else: failureToInferStaticParam(c.c.config, exp) + result = isNone if lowerBound.kind == nkIntLit: if upperBound.kind == nkIntLit: if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1: @@ -2465,7 +2477,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int a = 1 # iterates over the actual given arguments f = if m.callee.kind != tyGenericBody: 1 else: 0 # iterates over formal parameters - arg: PNode # current prepared argument + arg: PNode = nil # current prepared argument formalLen = m.callee.n.len formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter container: PNode = nil # constructed container @@ -2726,6 +2738,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; else: if f.kind in {tyVar}: f = f.lastSon if typeRel(m, f, t) == isNone: + result = nil localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") else: result = c.semGenerateInstance(c, dc, m.bindings, info) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index e07d55fbcd73..99e4342bbb89 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -503,6 +503,7 @@ template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode = elif node[1].kind == nkCheckedFieldExpr: dotExpr = node[1][0] else: + dotExpr = nil localError(config, node.info, "can't compute offsetof on this ast") assert dotExpr != nil diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index 2fcc50bbe984..b0b6fea2ef0e 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -70,6 +70,7 @@ func encode*(values: seq[int]): string {.raises: [].} = shift = 5 continueBit = 1 shl 5 mask = continueBit - 1 + result = "" for val in values: # Sign is stored in first bit var newVal = abs(val) shl 1 @@ -101,8 +102,8 @@ iterator tokenize*(line: string): (int, string) = token = "" while col < line.len: var - token: string - name: string + token: string = "" + name: string = "" # First we find the next identifier col += line.skipWhitespace(col) col += line.skipUntil(IdentStartChars, col) @@ -110,7 +111,7 @@ iterator tokenize*(line: string): (int, string) = col += line.parseIdent(token, col) # Idents will either be originalName_randomInt or HEXhexCode_randomInt if token.startsWith("HEX"): - var hex: int + var hex: int = 0 # 3 = "HEX".len and we only want to parse the two integers after it discard token[3 ..< 5].parseHex(hex) name = $chr(hex) @@ -125,6 +126,7 @@ iterator tokenize*(line: string): (int, string) = func parse*(source: string): SourceInfo = ## Parses the JS output for embedded line info ## So it can convert those into a series of mappings + result = default(SourceInfo) var skipFirstLine = true currColumn = 0 @@ -133,9 +135,9 @@ func parse*(source: string): SourceInfo = # Add each line as a node into the output for line in source.splitLines(): var - lineNumber: int - linePath: string - column: int + lineNumber: int = 0 + linePath: string = "" + column: int = 0 if line.strip().scanf("/* line $i:$i \"$+\" */", lineNumber, column, linePath): # When we reach the first line mappinsegmentg then we can assume # we can map the rest of the JS lines to Nim lines diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 7423fdfaa46d..e6c089966f49 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -114,7 +114,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; idgen: IdGenerator; spawnKind: TSpawnResult, result: PSym) = var body = newNodeI(nkStmtList, f.info) - var threadLocalBarrier: PSym + var threadLocalBarrier: PSym = nil if barrier != nil: var varSection2 = newNodeI(nkVarSection, barrier.info) threadLocalBarrier = addLocalVar(g, varSection2, nil, idgen, result, @@ -122,7 +122,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; body.add varSection2 body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info, threadLocalBarrier.newSymNode) - var threadLocalProm: PSym + var threadLocalProm: PSym = nil if spawnKind == srByVar: threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv) elif fv != nil: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index ca19b2460563..c7efad1af64e 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -55,6 +55,8 @@ proc findDocComment(n: PNode): PNode = result = findDocComment(n[1]) elif n.kind in {nkAsgn, nkFastAsgn, nkSinkAsgn} and n.len == 2: result = findDocComment(n[1]) + else: + result = nil proc extractDocComment(g: ModuleGraph; s: PSym): string = var n = findDocComment(s.ast) @@ -106,7 +108,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0: result = 0 else: - var sourceIdent: string + var sourceIdent: string = "" result = parseWhile(line, sourceIdent, OpChars + {'[', '(', '{', ']', ')', '}'}, column) if ident[^1] == '=' and ident[0] in linter.Letters: @@ -254,13 +256,17 @@ proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} = of nkOpenSymChoice, nkClosedSymChoice, nkAccQuoted: if n.len > 0: result = prefixMatch(s, n[0]) - else: discard + else: + result = default(PrefixMatch) + else: result = default(PrefixMatch) if s.kind != skModule: if prefix != nil: res = prefixMatch(s, prefix) result = res != PrefixMatch.None else: result = true + else: + result = false proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} = result = filterSym(s, prefix, res) and s.name.s[0] in lexer.SymChars and @@ -294,7 +300,7 @@ proc getQuality(s: PSym): range[0..100] = result = result - 5 proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) = - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSym(s, f, pm) and fieldVisible(c, s): outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info, s.getQuality, pm, c.inTypeContext > 0, 0)) @@ -302,7 +308,7 @@ proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var template wholeSymTab(cond, section: untyped) {.dirty.} = for (item, scopeN, isLocal) in uniqueSyms(c): let it = item - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if cond: outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) @@ -365,6 +371,8 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return result = sigmatch.argtypeMatches(c, s.typ[1], firstArg) + else: + result = false proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) = assert typ != nil @@ -374,7 +382,7 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = # do not produce too many symbols: for (it, scopeN, isLocal) in uniqueSyms(c): - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSym(it, f, pm): outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info, it.getQuality, pm, c.inTypeContext > 0, scopeN)) @@ -383,7 +391,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but # ``myObj``. var typ = n.typ - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) when defined(nimsuggest): if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0: # consider 'foo.|' where 'foo' is some not imported module. @@ -445,7 +453,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) for node in typ.n: if node.kind == nkSym: let s = node.sym - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSym(s, field, pm): outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, n.info, s.getQuality, pm, c.inTypeContext > 0, 0)) @@ -460,17 +468,24 @@ type proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult = if current.fileIndex == trackPos.fileIndex: + result = cpNone if current.line == trackPos.line and abs(current.col-trackPos.col) < 4: return cpExact if current.line >= trackPos.line: return cpFuzzy + else: + result = cpNone proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool = if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line: let col = trackPos.col if col >= current.col and col <= current.col+tokenLen-1: - return true + result = true + else: + result = false + else: + result = false when defined(nimsuggest): # Since TLineInfo defined a == operator that doesn't include the column, @@ -700,7 +715,7 @@ proc suggestSentinel*(c: PContext) = var outputs: Suggestions = @[] # suggest everything: for (it, scopeN, isLocal) in uniqueSyms(c): - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSymNoOpr(it, nil, pm): outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), it.getQuality, diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index c00fe8b6771b..1c8acf2a6420 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -36,6 +36,8 @@ proc containsShebang(s: string, i: int): bool = var j = i + 2 while j < s.len and s[j] in Whitespace: inc(j) result = s[j] == '/' + else: + result = false proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache; config: ConfigRef): PNode = @@ -64,6 +66,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache llStreamClose(s) proc getFilter(ident: PIdent): FilterKind = + result = filtNone for i in FilterKind: if cmpIgnoreStyle(ident.s, $i) == 0: return i @@ -74,6 +77,7 @@ proc getCallee(conf: ConfigRef; n: PNode): PIdent = elif n.kind == nkIdent: result = n.ident else: + result = nil localError(conf, n.info, "invalid filter: " & renderTree(n)) proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile, @@ -124,7 +128,7 @@ proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream; proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): bool = let filename = toFullPathConsiderDirty(config, fileIdx) - var f: File + var f: File = default(File) if not open(f, filename.string): rawMessage(config, errGenerated, "cannot open file: " & filename.string) return false @@ -132,7 +136,9 @@ proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache; result = true proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode = - var p: Parser + var p: Parser = default(Parser) if setupParser(p, fileIdx, cache, config): result = parseAll(p) closeParser(p) + else: + result = nil diff --git a/compiler/transf.nim b/compiler/transf.nim index d0428b725d4a..92d740276bbc 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -242,9 +242,10 @@ proc transformConstSection(c: PTransf, v: PNode): PNode = proc hasContinue(n: PNode): bool = case n.kind - of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard + of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: result = false of nkContinueStmt: result = true else: + result = false for i in 0.. 0: n[0] else: n @@ -145,12 +156,14 @@ proc isNoSideEffectPragma*(n: PNode): bool = result = k == wNoSideEffect proc findPragma*(n: PNode, which: TSpecialWord): PNode = + result = nil if n.kind == nkPragma: for son in n: if whichPragma(son) == which: return son proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode = + result = nil for i in 0.. 0: assert(t.n[0].kind == nkSym) result = toInt128(t.n[0].sym.position) + else: + result = Zero of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) - else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') + else: + result = Zero + internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') of tyUncheckedArray, tyCstring: result = Zero else: - internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') result = Zero + internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') proc firstFloat*(t: PType): BiggestFloat = case t.kind @@ -834,14 +840,14 @@ proc targetSizeSignedToKind*(conf: ConfigRef): TTypeKind = of 8: result = tyInt64 of 4: result = tyInt32 of 2: result = tyInt16 - else: discard + else: result = tyNone proc targetSizeUnsignedToKind*(conf: ConfigRef): TTypeKind = case conf.target.intSize of 8: result = tyUInt64 of 4: result = tyUInt32 of 2: result = tyUInt16 - else: discard + else: result = tyNone proc normalizeKind*(conf: ConfigRef, k: TTypeKind): TTypeKind = case k @@ -869,7 +875,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = of 4: result = toInt128(0x7FFFFFFF) of 2: result = toInt128(0x00007FFF) of 1: result = toInt128(0x0000007F) - else: discard + else: result = Zero else: result = toInt128(0x7FFFFFFFFFFFFFFF'u64) of tyInt8: result = toInt128(0x0000007F) of tyInt16: result = toInt128(0x00007FFF) @@ -889,18 +895,22 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = if t.n.len > 0: assert(t.n[^1].kind == nkSym) result = toInt128(t.n[^1].sym.position) + else: + result = Zero of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = lastOrd(conf, lastSon(t)) of tyProxy: result = Zero of tyOrdinal: if t.len > 0: result = lastOrd(conf, lastSon(t)) - else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') + else: + result = Zero + internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') of tyUncheckedArray: result = Zero else: - internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') result = Zero + internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') proc lastFloat*(t: PType): BiggestFloat = case t.kind @@ -972,7 +982,7 @@ type proc initSameTypeClosure: TSameTypeClosure = # we do the initialization lazily for performance (avoids memory allocations) - discard + result = TSameTypeClosure() proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = result = c.s.len > 0 and c.s.contains((a.id, b.id)) @@ -1011,6 +1021,8 @@ proc equalParam(a, b: PSym): TParamsEquality = result = paramsEqual elif b.ast != nil: result = paramsIncompatible + else: + result = paramsNotEqual else: result = paramsNotEqual @@ -1078,6 +1090,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = return false elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags: result = false + else: + result = false template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = if tfFromGeneric notin a.flags + b.flags: @@ -1097,6 +1111,8 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = if tfFromGeneric in a.flags * b.flags and a.sym.id == b.sym.id: # ok, we need the expensive structural check body + else: + result = false proc sameObjectTypes*(a, b: PType): bool = # specialized for efficiency (sigmatch uses it) @@ -1134,6 +1150,12 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = for i in 0.. 1: # we know the result is derived from the first argument: - var roots: seq[(PSym, int)] + var roots: seq[(PSym, int)] = @[] allRoots(n[1], roots, RootEscapes) for r in roots: connect(c, dest.sym, r[0], n[1].info) @@ -618,7 +620,8 @@ proc deps(c: var Partitions; dest, src: PNode) = if borrowChecking in c.goals: borrowingAsgn(c, dest, src) - var targets, sources: seq[(PSym, int)] + var targets: seq[(PSym, int)] = @[] + var sources: seq[(PSym, int)] = @[] allRoots(dest, targets, 0) allRoots(src, sources, 0) @@ -668,7 +671,7 @@ proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = if constParameters in c.goals and tfNoSideEffect in callee.flags: discard "we know there are no hidden mutations through an immutable parameter" elif c.inNoSideEffectSection == 0 and containsPointer(n.typ): - var roots: seq[(PSym, int)] + var roots: seq[(PSym, int)] = @[] allRoots(n, roots, RootEscapes) for r in roots: potentialMutation(c, r[0], r[1], n.info) @@ -716,7 +719,7 @@ proc traverse(c: var Partitions; n: PNode) = if i < L: let paramType = parameters[i].skipTypes({tyGenericInst, tyAlias}) if not paramType.isCompileTimeOnly and paramType.kind in {tyVar, tySink, tyOwned}: - var roots: seq[(PSym, int)] + var roots: seq[(PSym, int)] = @[] allRoots(it, roots, RootEscapes) if paramType.kind == tyVar: if c.inNoSideEffectSection == 0: diff --git a/compiler/vm.nim b/compiler/vm.nim index 7376ff165ee8..1f4e4333d0f3 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -381,6 +381,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = return pc + 1 proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = + result = false if desttyp.kind == tyString: dest.ensureKind(rkNode) dest.node = newNode(nkStrLit) @@ -548,11 +549,12 @@ proc takeCharAddress(c: PCtx, src: PNode, index: BiggestInt, pc: int): TFullReg proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = + result = TFullReg(kind: rkNone) var pc = start var tos = tos # Used to keep track of where the execution is resumed. var savedPC = -1 - var savedFrame: PStackFrame + var savedFrame: PStackFrame = nil when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): template updateRegsAlias = discard template regs: untyped = tos.slots @@ -1381,7 +1383,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let prcValue = c.globals[prc.position-1] if prcValue.kind == nkEmpty: globalError(c.config, c.debug[pc], "cannot run " & prc.name.s) - var slots2: TNodeSeq + var slots2: TNodeSeq = default(TNodeSeq) slots2.setLen(tos.slots.len) for i in 0..= 0 # 'nim check' does not like this internalAssert. if tmp >= 0: result = TRegister(tmp) + else: + result = 0 proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. @@ -830,10 +832,14 @@ proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = proc isInt8Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int8) and n.intVal <= high(int8) + else: + result = false proc isInt16Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int16) and n.intVal <= high(int16) + else: + result = false proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = if n[2].isInt8Lit: @@ -854,7 +860,9 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = # xxx consider whether to use t2 and targ2 here if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: # don't do anything for lambda lifting conversions: - return true + result = true + else: + result = false if implicitConv(): gen(c, arg, dest) @@ -1419,6 +1427,7 @@ proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = + result = nil case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: var m = n[0][0] @@ -1500,6 +1509,7 @@ proc cannotEval(c: PCtx; n: PNode) {.noinline.} = n.renderTree) proc isOwnedBy(a, b: PSym): bool = + result = false var a = a.owner while a != nil and a.kind != skModule: if a == b: return true @@ -1512,7 +1522,9 @@ proc getOwner(c: PCtx): PSym = proc importcCondVar*(s: PSym): bool {.inline.} = # see also importcCond if sfImportc in s.flags: - return s.kind in {skVar, skLet, skConst} + result = s.kind in {skVar, skLet, skConst} + else: + result = false proc checkCanEval(c: PCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: @@ -1635,6 +1647,7 @@ proc isEmptyBody(n: PNode): bool = proc importcCond*(c: PCtx; s: PSym): bool {.inline.} = ## return true to importc `s`, false to execute its body instead (refs #8405) + result = false if sfImportc in s.flags: if s.kind in routineKinds: return isEmptyBody(getBody(c.graph, s)) @@ -2048,6 +2061,7 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = proc genProc*(c: PCtx; s: PSym): int proc toKey(s: PSym): string = + result = "" var s = s while s != nil: result.add s.name.s @@ -2334,7 +2348,7 @@ proc genProc(c: PCtx; s: PSym): int = #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 - var eofInstr: TInstr + var eofInstr: TInstr = default(TInstr) if last >= 0 and c.code[last].opcode == opcEof: eofInstr = c.code[last] c.code.setLen(last) diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 1741574b8661..7d9e66104cc1 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -69,7 +69,9 @@ proc getVar*(a: VmArgs; i: Natural): PNode = case p.kind of rkRegisterAddr: result = p.regAddr.node of rkNodeAddr: result = p.nodeAddr[] - else: doAssert false, $p.kind + else: + result = nil + doAssert false, $p.kind proc getNodeAddr*(a: VmArgs; i: Natural): PNode = let nodeAddr = getX(rkNodeAddr, nodeAddr) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index b48197aefad2..e1e69f6cc503 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -21,6 +21,7 @@ proc ptrToInt(x: PNode): int {.inline.} = proc getField(n: PNode; position: int): PSym = case n.kind of nkRecList: + result = nil for i in 0.. infoMax.time: infoMax = info diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index 89e532951a4d..bcfdf37c2b42 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -59,7 +59,7 @@ proc initHeapQueue*[T](): HeapQueue[T] = ## ## **See also:** ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_ - discard + result = default(HeapQueue[T]) proc len*[T](heap: HeapQueue[T]): int {.inline.} = ## Returns the number of elements of `heap`. diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 19bc3e65c6a6..e2adba910ebe 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -860,7 +860,7 @@ template toSeq*(iter: untyped): untyped = inc i result else: - var result: seq[typeof(iter)]# = @[] + var result: seq[typeof(iter)] = @[] for x in iter: result.add(x) result diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim index 7ebd2276047d..dbd4ce1d5e12 100644 --- a/lib/pure/collections/setimpl.nim +++ b/lib/pure/collections/setimpl.nim @@ -62,6 +62,7 @@ template containsOrInclImpl() {.dirty.} = if index >= 0: result = true else: + result = false if mustRehash(s): enlarge(s) index = rawGetKnownHC(s, key, hc) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 11e324923456..7e193af1a7ec 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -128,7 +128,7 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] = var a = initHashSet[int]() a.incl(3) assert len(a) == 1 - + result = default(HashSet[A]) result.init(initialSize) proc `[]`*[A](s: var HashSet[A], key: A): var A = diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index fa06b99234c4..112aaa7d0697 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -56,14 +56,14 @@ template maybeRehashPutImpl(enlarge) {.dirty.} = template putImpl(enlarge) {.dirty.} = checkIfInitialized() - var hc: Hash + var hc: Hash = default(Hash) var index = rawGet(t, key, hc) if index >= 0: t.data[index].val = val else: maybeRehashPutImpl(enlarge) template mgetOrPutImpl(enlarge) {.dirty.} = checkIfInitialized() - var hc: Hash + var hc: Hash = default(Hash) var index = rawGet(t, key, hc) if index < 0: # not present: insert (flipping index) @@ -73,7 +73,7 @@ template mgetOrPutImpl(enlarge) {.dirty.} = template hasKeyOrPutImpl(enlarge) {.dirty.} = checkIfInitialized() - var hc: Hash + var hc: Hash = default(Hash) var index = rawGet(t, key, hc) if index < 0: result = false @@ -210,3 +210,5 @@ template equalsImpl(s, t: typed) = if not t.hasKey(key): return false if t.getOrDefault(key) != val: return false return true + else: + return false diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 53490c911a28..d4056897db67 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -418,7 +418,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B = let a = {'a': 5, 'b': 9}.toTable doAssert a.getOrDefault('a') == 5 doAssert a.getOrDefault('z') == 0 - + result = default(B) getOrDefaultImpl(t, key) proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = @@ -436,7 +436,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = let a = {'a': 5, 'b': 9}.toTable doAssert a.getOrDefault('a', 99) == 5 doAssert a.getOrDefault('z', 99) == 99 - + result = default(B) getOrDefaultImpl(t, key, default) proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = @@ -1463,7 +1463,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = let a = {'a': 5, 'b': 9}.toOrderedTable doAssert a.getOrDefault('a') == 5 doAssert a.getOrDefault('z') == 0 - + result = default(B) getOrDefaultImpl(t, key) proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = @@ -1481,7 +1481,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = let a = {'a': 5, 'b': 9}.toOrderedTable doAssert a.getOrDefault('a', 99) == 5 doAssert a.getOrDefault('z', 99) == 99 - + result = default(B) getOrDefaultImpl(t, key, default) proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index 04fa78ada9a2..c6d007c26198 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -198,6 +198,7 @@ proc contains*[A](s: PackedSet[A], key: A): bool = assert B notin letters if s.elems <= s.a.len: + result = false for i in 0.. Date: Sun, 6 Aug 2023 23:59:43 +0800 Subject: [PATCH 124/347] unify starting blank lines in the experimental manual (#22396) unify starting blank lines in the experimental manal --- doc/manual_experimental.md | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 4ee035b65d1b..8cddce4f1437 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -1976,11 +1976,9 @@ With `experimental: "strictDefs"`, `let` statements are allowed to not have an i An `out` parameter is like a `var` parameter but it must be written to before it can be used: ```nim - proc myopen(f: out File; name: string): bool = f = default(File) result = open(f, name) - ``` While it is usually the better style to use the return type in order to return results API and ABI @@ -1988,9 +1986,7 @@ considerations might make this infeasible. Like for `var T` Nim maps `out T` to For example POSIX's `stat` routine can be wrapped as: ```nim - proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "".} - ``` When the implementation of a routine with output parameters is analysed, the compiler @@ -1998,13 +1994,11 @@ checks that every path before the (implicit or explicit) return does set every o parameter: ```nim - proc p(x: out int; y: out string; cond: bool) = x = 4 if cond: y = "abc" # error: not every path initializes 'y' - ``` @@ -2014,11 +2008,9 @@ Out parameters and exception handling The analysis should take exceptions into account (but currently does not): ```nim - proc p(x: out int; y: out string; cond: bool) = x = canRaise(45) y = "abc" # <-- error: not every path initializes 'y' - ``` Once the implementation takes exceptions into account it is easy enough to @@ -2030,7 +2022,6 @@ Out parameters and inheritance It is not valid to pass an lvalue of a supertype to an `out T` parameter: ```nim - type Superclass = object of RootObj a: int @@ -2043,7 +2034,6 @@ It is not valid to pass an lvalue of a supertype to an `out T` parameter: var v: Subclass init v use v.s # the 's' field was never initialized! - ``` However, in the future this could be allowed and provide a better way to write object @@ -2167,14 +2157,12 @@ inside `Isolated[T]`. It is what a channel implementation should use in order to the freedom of data races: ```nim - proc send*[T](c: var Channel[T]; msg: sink Isolated[T]) proc recv*[T](c: var Channel[T]): T ## Note: Returns T, not Isolated[T] for convenience. proc recvIso*[T](c: var Channel[T]): Isolated[T] ## remembers the data is Isolated[T]. - ``` In order to create an `Isolated` graph one has to use either `isolate` or `unsafeIsolate`. @@ -2187,9 +2175,7 @@ is free of external aliases into it. `isolate` ensures this invariant. It is inspired by Pony's `recover` construct: ```nim - func isolate(x: sink T): Isolated[T] {.magic: "Isolate".} - ``` @@ -2251,7 +2237,6 @@ encapsulates a `ref` type effectively so that a variable of this container type can be used in an `isolate` context: ```nim - type Isolated*[T] {.sendable.} = object ## Isolated data can only be moved, not copied. value: T @@ -2265,7 +2250,6 @@ can be used in an `isolate` context: proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} = # delegate to value's destroy operation `=destroy`(dest.value) - ``` The `.sendable` pragma itself is an experimenal, unchecked, unsafe annotation. It is @@ -2279,7 +2263,6 @@ Virtual pragma Here's an example of how to use the virtual pragma: ```nim - proc newCpp*[T](): ptr T {.importcpp: "new '*0()".} type Foo = object of RootObj @@ -2300,7 +2283,6 @@ let booAsFoo = cast[FooPtr](newCpp[Boo]()) foo.salute() # prints hello foo boo.salute() # prints hello boo booAsFoo.salute() # prints hello boo - ``` In this example, the `salute` function is virtual in both Foo and Boo types. This allows for polymorphism. @@ -2355,13 +2337,11 @@ The `constructor` pragma can be used in two ways: in conjunction with `importcpp Consider: ```nim - type Foo* = object x: int32 proc makeFoo(x: int32): Foo {.constructor.} = this.x = x - ``` It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. @@ -2372,8 +2352,6 @@ Like `virtual`, `constructor` also supports a syntax that allows to express C++ For example: ```nim - - {.emit:"""/*TYPESECTION*/ struct CppClass { int x; @@ -2398,7 +2376,6 @@ proc makeNimClass(x: int32): NimClass {.constructor:"NimClass('1 #1) : CppClass( # Optional: define the default constructor explicitly proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = this.x = 1 - ``` In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. From 26eb0a944fe6a0a5d09798262055aa30c9b0001a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 7 Aug 2023 15:40:39 +0800 Subject: [PATCH 125/347] a bit modern code for depends (#22400) * a bit modern code for depends * simplify --- compiler/depends.nim | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/depends.nim b/compiler/depends.nim index 6c7ee8ffc325..cb462e188858 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -106,11 +106,6 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) = changeFileExt(project, "dot")) proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = - var g: PGen - new(g) - g.module = module - g.config = graph.config - g.graph = graph + result = PGen(module: module, config: graph.config, graph: graph) if graph.backend == nil: graph.backend = Backend(dotGraph: "") - result = g From 614a18cd05bda525f62310578115ecc6c41b7e09 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 7 Aug 2023 15:49:30 +0800 Subject: [PATCH 126/347] Delete parse directory, which was pushed wrongly before [backport] (#22401) Delete parse directory --- parse/pragmas.nim | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 parse/pragmas.nim diff --git a/parse/pragmas.nim b/parse/pragmas.nim deleted file mode 100644 index bf77a28420e6..000000000000 --- a/parse/pragmas.nim +++ /dev/null @@ -1,3 +0,0 @@ -# parse/pragmas.nim content - -proc foo*() = discard \ No newline at end of file From fe9ae2c69adc39cd170b4bd31221fb66135fd571 Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 7 Aug 2023 16:09:35 +0800 Subject: [PATCH 127/347] nimIoselector option (#22395) * selectors.nim: Add define to select event loop implementation * rename to nimIoselector --------- Co-authored-by: Jan Pobrislo --- lib/pure/selectors.nim | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 2d10e3f3211a..fcee22c09ff4 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -344,7 +344,18 @@ else: res = int(fdLim.rlim_cur) - 1 res - when defined(linux) and not defined(emscripten): + when defined(nimIoselector): + when nimIoselector == "epoll": + include ioselects/ioselectors_epoll + elif nimIoselector == "kqueue": + include ioselects/ioselectors_kqueue + elif nimIoselector == "poll": + include ioselects/ioselectors_poll + elif nimIoselector == "select": + include ioselects/ioselectors_select + else: + {.fatal: "Unknown nimIoselector specified by define.".} + elif defined(linux) and not defined(emscripten): include ioselects/ioselectors_epoll elif bsdPlatform: include ioselects/ioselectors_kqueue From b5b4b48c942b23991c8d11f41dc39b7e211e5b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Mon, 7 Aug 2023 09:11:00 +0100 Subject: [PATCH 128/347] [C++] Member pragma RFC (https://github.com/nim-lang/RFCs/issues/530) (#22272) * [C++] Member pragma RFC #530 rebase devel * changes the test so `echo` is not used before Nim is init * rebase devel * fixes Error: use explicit initialization of X for clarity [Uninit] --- compiler/ast.nim | 2 ++ compiler/ccgtypes.nim | 22 ++++++++++-------- compiler/cgen.nim | 4 ++-- compiler/pragmas.nim | 10 ++++---- compiler/semstmts.nim | 15 ++++++------ compiler/wordrecg.nim | 2 +- tests/cpp/tmember.nim | 53 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 tests/cpp/tmember.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index eccf5a985250..aba877187b38 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -314,6 +314,7 @@ type # an infinite loop, this flag is used as a sentinel to stop it. sfVirtual # proc is a C++ virtual function sfByCopy # param is marked as pass bycopy + sfMember # proc is a C++ member of a type sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl TSymFlags* = set[TSymFlag] @@ -347,6 +348,7 @@ const sfBase* = sfDiscriminant sfCustomPragma* = sfRegister # symbol is custom pragma template sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template + sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition const # getting ready for the future expr/stmt merge diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 205031a918f9..6bac84e95634 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -493,12 +493,12 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr template cgDeclFrmt*(s: PSym): string = s.constraint.strVal -proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var string, +proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string, check: var IntSet, declareEnvironment=true; weakDep=false;) = let t = prc.typ let isCtor = sfConstructor in prc.flags - if isCtor: + if isCtor or (name[0] == '~' and sfMember in prc.flags): #destructors cant have void rettype = "" elif t[0] == nil or isInvalidReturnType(m.config, t): rettype = "void" @@ -555,6 +555,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var multiFormat(params, @['\'', '#'], [types, names]) multiFormat(superCall, @['\'', '#'], [types, names]) + multiFormat(name, @['\'', '#'], [types, names]) #so we can ~'1 on members if params == "()": if types.len == 0: params = "(void)" @@ -1148,11 +1149,14 @@ proc isReloadable(m: BModule; prc: PSym): bool = proc isNonReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags -proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride: var bool; isCtor: bool) = +proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual: var bool; isCtor: bool) = var afterParams: string = "" if scanf(val, "$*($*)$s$*", name, params, afterParams): isFnConst = afterParams.find("const") > -1 isOverride = afterParams.find("override") > -1 + isMemberVirtual = name.find("virtual ") > -1 + if isMemberVirtual: + name = name.replace("virtual ", "") if isCtor: discard scanf(afterParams, ":$s$*", superCall) else: @@ -1161,9 +1165,8 @@ proc parseVFunctionDecl(val: string; name, params, retType, superCall: var strin params = "(" & params & ")" proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) = - assert {sfVirtual, sfConstructor} * prc.flags != {} + assert sfCppMember * prc.flags != {} let isCtor = sfConstructor in prc.flags - let isVirtual = not isCtor var check = initIntSet() fillBackendName(m, prc) fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown) @@ -1179,9 +1182,10 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = var typDesc = getTypeDescWeak(m, typ, check, dkParam) let asPtrStr = rope(if asPtr: "_PTR" else: "") var name, params, rettype, superCall: string = "" - var isFnConst, isOverride: bool = false - parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isCtor) - genMemberProcParams(m, prc, superCall, rettype, params, check, true, false) + var isFnConst, isOverride, isMemberVirtual: bool = false + parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor) + genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) + let isVirtual = sfVirtual in prc.flags or isMemberVirtual var fnConst, override: string = "" if isCtor: name = typDesc @@ -1194,7 +1198,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = override = " override" superCall = "" else: - if isVirtual: + if not isCtor: prc.loc.r = "$1$2(@)" % [memberOp, name] elif superCall != "": superCall = " : " & superCall diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2643e6edd157..a3b74c408e08 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1149,7 +1149,7 @@ proc isNoReturn(m: BModule; s: PSym): bool {.inline.} = proc genProcAux*(m: BModule, prc: PSym) = var p = newProc(prc, m) var header = newRopeAppender() - if m.config.backend == backendCpp and {sfVirtual, sfConstructor} * prc.flags != {}: + if m.config.backend == backendCpp and sfCppMember * prc.flags != {}: genMemberProcHeader(m, prc, header) else: genProcHeader(m, prc, header) @@ -1260,7 +1260,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) - if lfNoDecl in sym.loc.flags or {sfVirtual, sfConstructor} * sym.flags != {}: return + if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return if lfDynamicLib in sym.loc.flags: if sym.itemId.module != m.module.position and not containsOrIncl(m.declaredThings, sym.id): diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index e0fdba566b73..56e25c0b43d8 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -34,7 +34,7 @@ const wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, - wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky} + wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky, wMember} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, @@ -245,10 +245,10 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n) else: result = defaultStr -proc processVirtual(c: PContext, n: PNode, s: PSym) = +proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) = s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1")) s.constraint.strVal = s.constraint.strVal % s.name.s - s.flags.incl {sfVirtual, sfInfixCall, sfExportc, sfMangleCpp} + s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp} s.typ.callConv = ccNoConvention incl c.config.globalOptions, optMixedMode @@ -1284,7 +1284,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wSystemRaisesDefect: sym.flags.incl sfSystemRaisesDefect of wVirtual: - processVirtual(c, it, sym) + processVirtual(c, it, sym, sfVirtual) + of wMember: + processVirtual(c, it, sym, sfMember) else: invalidPragma(c, it) elif comesFromPush and whichKeyword(ident) != wInvalid: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 448c26cf2a97..302ccc8b9e1e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2269,26 +2269,27 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfBorrow in s.flags and c.config.cmd notin cmdDocLike: result[bodyPos] = c.graph.emptyNode - if {sfVirtual, sfConstructor} * s.flags != {} and sfImportc notin s.flags: + if sfCppMember * s.flags != {} and sfImportc notin s.flags: let isVirtual = sfVirtual in s.flags - let pragmaName = if isVirtual: "virtual" else: "constructor" + let isCtor = sfConstructor in s.flags + let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member" if c.config.backend == backendCpp: - if s.typ.sons.len < 2 and isVirtual: - localError(c.config, n.info, "virtual must have at least one parameter") + if s.typ.sons.len < 2 and not isCtor: + localError(c.config, n.info, pragmaName & " must have at least one parameter") for son in s.typ.sons: if son!=nil and son.isMetaType: localError(c.config, n.info, pragmaName & " unsupported for generic routine") var typ: PType - if sfConstructor in s.flags: + if isCtor: typ = s.typ.sons[0] if typ == nil or typ.kind != tyObject: localError(c.config, n.info, "constructor must return an object") else: typ = s.typ.sons[1] - if typ.kind == tyPtr and isVirtual: + if typ.kind == tyPtr and not isCtor: typ = typ[0] if typ.kind != tyObject: - localError(c.config, n.info, "virtual must be either ptr to object or object type.") + localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.") if typ.owner.id == s.owner.id and c.module.id == s.owner.id: c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s else: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index f784f0a75455..b2b0c8ae2389 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -103,7 +103,7 @@ type wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", - wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", + wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", wMember = "member", wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", diff --git a/tests/cpp/tmember.nim b/tests/cpp/tmember.nim new file mode 100644 index 000000000000..3f498c722488 --- /dev/null +++ b/tests/cpp/tmember.nim @@ -0,0 +1,53 @@ +discard """ + targets: "cpp" + cmd: "nim cpp $file" + output: ''' +2 +false +hello foo +hello boo +hello boo +destructing +destructing +''' +""" +proc print(s: cstring) {.importcpp:"printf(@)", header:"".} + +type + Doo {.exportc.} = object + test: int + +proc memberProc(f: Doo) {.exportc, member.} = + echo $f.test + +proc destructor(f: Doo) {.member: "~'1()", used.} = + print "destructing\n" + +proc `==`(self, other: Doo): bool {.member:"operator==('2 const & #2) const -> '0"} = + self.test == other.test + +let doo = Doo(test: 2) +doo.memberProc() +echo doo == Doo(test: 1) + +#virtual +proc newCpp*[T](): ptr T {.importcpp:"new '*0()".} +type + Foo = object of RootObj + FooPtr = ptr Foo + Boo = object of Foo + BooPtr = ptr Boo + +proc salute(self: FooPtr) {.member: "virtual $1()".} = + echo "hello foo" + +proc salute(self: BooPtr) {.member: "virtual $1()".} = + echo "hello boo" + +let foo = newCpp[Foo]() +let boo = newCpp[Boo]() +let booAsFoo = cast[FooPtr](newCpp[Boo]()) + +foo.salute() +boo.salute() +booAsFoo.salute() From 260b4236fca566530c8327c24e5034295d0b7edc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 7 Aug 2023 16:11:59 +0800 Subject: [PATCH 129/347] use out parameters for getTemp (#22399) --- compiler/ccgcalls.nim | 12 +++++------- compiler/ccgexprs.nim | 28 +++++++++++++++++----------- compiler/ccgreset.nim | 2 +- compiler/ccgstmts.nim | 3 ++- compiler/ccgtrav.nim | 9 ++++----- compiler/cgen.nim | 28 ++++++++++------------------ 6 files changed, 39 insertions(+), 43 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 8661ed83373a..dca581fadc1f 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -96,7 +96,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl.add(");\n") line(p, cpsStmts, pl) else: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, typ[0], tmp, needsInit=true) pl.add(addrLoc(p.config, tmp)) pl.add(");\n") @@ -133,7 +133,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) else: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, typ[0], tmp, needsInit=true) var list: TLoc = default(TLoc) initLoc(list, locCall, d.lode, OnUnknown) @@ -273,14 +273,12 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = # Also don't regress for non ARC-builds, too risky. if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and getSize(p.config, a.lode.typ) < 1024: - result = default(TLoc) getTemp(p, a.lode.typ, result, needsInit=false) genAssignment(p, result, a, {}) else: result = a proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc = - result = default(TLoc) getTemp(p, a.lode.typ, result, needsInit=false) genAssignment(p, result, a, {}) @@ -483,7 +481,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genCallPattern() if canRaise: raiseExit(p) else: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, typ[0], tmp, needsInit=true) pl.add(addrLoc(p.config, tmp)) genCallPattern() @@ -501,7 +499,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) else: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, typ[0], tmp) assert(d.t != nil) # generate an assignment to d: var list: TLoc = default(TLoc) @@ -782,7 +780,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = pl.add("];\n") line(p, cpsStmts, pl) else: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, typ[0], tmp, needsInit=true) pl.add(addrLoc(p.config, tmp)) pl.add("];\n") diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a285b86a67b6..cc0d0465c5ed 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -348,7 +348,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc]) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, ty, tmp) linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", [dest.rdLoc, src.rdLoc, tmp.rdLoc]) @@ -431,7 +431,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genDeepCopy(p: BProc; dest, src: TLoc) = template addrLocOrTemp(a: TLoc): Rope = if a.k == locExpr: - var tmp: TLoc = default(TLoc) + var tmp: TLoc getTemp(p, a.t, tmp) genAssignment(p, tmp, a, {}) addrLoc(p.config, tmp) @@ -1197,7 +1197,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: var L: TLabel - tmp: TLoc = default(TLoc) + tmp: TLoc getTemp(p, e.typ, tmp) # force it into a temp! inc p.splitDecls expr(p, e[1], tmp) @@ -1276,7 +1276,8 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # appendChar(tmp0, 'z'); # asgn(s, tmp0); # } - var a, tmp: TLoc = default(TLoc) + var a = default(TLoc) + var tmp: TLoc getTemp(p, e.typ, tmp) var L = 0 var appends: Rope = "" @@ -1556,7 +1557,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or (isPartOf(d.lode, e) != arNo) - var tmp: TLoc = default(TLoc) + var tmp: TLoc = TLoc() var r: Rope if useTemp: getTemp(p, t, tmp) @@ -1604,7 +1605,8 @@ proc lhsDoesAlias(a, b: PNode): bool = if isPartOf(a, y) != arNo: return true proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = - var arr, tmp: TLoc = default(TLoc) + var arr = default(TLoc) + var tmp: TLoc = default(TLoc) # bug #668 let doesAlias = lhsDoesAlias(d.lode, n) let dest = if doesAlias: addr(tmp) else: addr(d) @@ -1669,7 +1671,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit]) genAssignment(p, elem, arr, {needToCopy}) else: - var i: TLoc = default(TLoc) + var i: TLoc getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L]) initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) @@ -1986,7 +1988,8 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = # b = temp cowBracket(p, e[1]) cowBracket(p, e[2]) - var a, b, tmp: TLoc = default(TLoc) + var a, b = default(TLoc) + var tmp: TLoc getTemp(p, skipTypes(e[1].typ, abstractVar), tmp) initLocExpr(p, e[1], a) # eval a initLocExpr(p, e[2], b) # eval b @@ -2090,7 +2093,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "&", "|", "& ~"] - var a, b, i: TLoc = default(TLoc) + var a, b = default(TLoc) + var i: TLoc var setType = skipTypes(e[1].typ, abstractVar) var size = int(getSize(p.config, setType)) case size @@ -2645,7 +2649,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var - a, b, idx: TLoc = default(TLoc) + a, b = default(TLoc) + var idx: TLoc if nfAllConst in e.flags: var elem = newRopeAppender() genSetNode(p, e, elem) @@ -2744,7 +2749,8 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = p.module.s[cfsData].add data putIntoDest(p, d, n, tmp, OnStatic) else: - var tmp, a, b: TLoc = default(TLoc) + var tmp: TLoc + var a, b = default(TLoc) initLocExpr(p, n[0], a) initLocExpr(p, n[1], b) if n[0].skipConv.kind == nkClosure: diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index f486f71fb722..5e6456704dd2 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -57,7 +57,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = specializeResetT(p, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(p.config, typ[0]) - var i: TLoc = default(TLoc) + var i: TLoc getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, arraySize]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 45104399a18c..1751321f3ffa 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1611,7 +1611,8 @@ when false: expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = - var a, tmp: TLoc = default(TLoc) + var a = default(TLoc) + var tmp: TLoc var dotExpr = e[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] initLocExpr(p, e[0], a) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index e4008bfc1eea..9af33d45e886 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -21,7 +21,7 @@ const proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) proc genCaseRange(p: BProc, branch: PNode) -proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) +proc getTemp(p: BProc, t: PType, result: out TLoc; needsInit=false) proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; typ: PType) = @@ -74,7 +74,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = genTraverseProc(c, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(c.p.config, typ[0]) - var i: TLoc = default(TLoc) + var i: TLoc getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) var oldCode = p.s(cpsStmts) freeze oldCode @@ -119,12 +119,11 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var p = c.p assert typ.kind == tySequence - var i: TLoc = default(TLoc) + var i: TLoc getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) var oldCode = p.s(cpsStmts) freeze oldCode - var a: TLoc = default(TLoc) - a.r = accessor + var a: TLoc = TLoc(r: accessor) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, lenExpr(c.p, a)]) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a3b74c408e08..0a54256523e9 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -541,17 +541,14 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = if not immediateAsgn: constructLoc(p, v.loc) -proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = +proc getTemp(p: BProc, t: PType, result: out TLoc; needsInit=false) = inc(p.labels) - result.r = "T" & rope(p.labels) & "_" + result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, dkVar), result.r]) else: linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) - result.k = locTemp - result.lode = lodeTyp t - result.storage = OnStack - result.flags = {} constructLoc(p, result, not needsInit) when false: # XXX Introduce a compiler switch in order to detect these easily. @@ -562,23 +559,18 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo writeStackTrace() -proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) = +proc getTempCpp(p: BProc, t: PType, result: out TLoc; value: Rope) = inc(p.labels) - result.r = "T" & rope(p.labels) & "_" + result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + storage: OnStack, flags: {}) linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value]) - result.k = locTemp - result.lode = lodeTyp t - result.storage = OnStack - result.flags = {} -proc getIntTemp(p: BProc, result: var TLoc) = +proc getIntTemp(p: BProc, result: out TLoc) = inc(p.labels) - result.r = "T" & rope(p.labels) & "_" + result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, + storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt), + flags: {}) linefmt(p, cpsLocals, "NI $1;$n", [result.r]) - result.k = locTemp - result.storage = OnStack - result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt) - result.flags = {} proc localVarDecl(p: BProc; n: PNode): Rope = result = "" From b4b555d8d10fa1277e57ded0dcfc4678bfafadb5 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 8 Aug 2023 11:13:38 +0800 Subject: [PATCH 130/347] tiny change on action.nim (#22405) --- ci/action.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/action.nim b/ci/action.nim index 5d3a50fda24d..ad0f4df0b65f 100644 --- a/ci/action.nim +++ b/ci/action.nim @@ -3,7 +3,7 @@ import std/[strutils, os, osproc, parseutils, strformat] proc main() = var msg = "" - const cmd = "./koch boot --gc:orc -d:release" + const cmd = "./koch boot --mm:orc -d:release" let (output, exitCode) = execCmdEx(cmd) From 0219c5a60740689f343f3261611219425e9f9531 Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 8 Aug 2023 12:13:14 +0800 Subject: [PATCH 131/347] fix #22287 nimlf_ undefined error (#22382) --- compiler/cgen.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0a54256523e9..e21d85a07244 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -301,6 +301,7 @@ proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) = genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex) proc genLineDir(p: BProc, t: PNode) = + if p == p.module.preInitProc: return let line = t.info.safeLineNm if optEmbedOrigSrc in p.config.globalOptions: From 47d06d3d4cb85a0c7c2273864f6088a5e8521f44 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 8 Aug 2023 13:42:08 +0800 Subject: [PATCH 132/347] fixes #22387; Undefined behavior when with hash(...) (#22404) * fixes #22387; Undefined behavior when with hash(...) * fixes vm * fixes nimscript --- lib/pure/hashes.nim | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index daa7f936612f..ad164d6d3110 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -319,16 +319,24 @@ proc murmurHash(x: openArray[byte]): Hash = h1: uint32 i = 0 + + template impl = + var j = stepSize + while j > 0: + dec j + k1 = (k1 shl 8) or (ord(x[i+j])).uint32 + # body while i < n * stepSize: var k1: uint32 - when defined(js) or defined(sparc) or defined(sparc64): - var j = stepSize - while j > 0: - dec j - k1 = (k1 shl 8) or (ord(x[i+j])).uint32 + + when nimvm: + impl() else: - k1 = cast[ptr uint32](unsafeAddr x[i])[] + when declared(copyMem): + copyMem(addr k1, addr x[i], 4) + else: + impl() inc i, stepSize k1 = imul(k1, c1) From 37d8f32ae9ead55a065ae42636e88265ea17ce4a Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 8 Aug 2023 16:06:47 +0800 Subject: [PATCH 133/347] =?UTF-8?q?fix=20#18823=20Passing=20Natural=20to?= =?UTF-8?q?=20bitops.BitsRange[T]=20parameter=20in=20generi=E2=80=A6=20(#2?= =?UTF-8?q?0683)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #18823 Passing Natural to bitops.BitsRange[T] parameter in generic proc is compile error --- compiler/ast.nim | 6 ++++-- compiler/semtypinst.nim | 13 +++++++------ compiler/sigmatch.nim | 8 ++++++-- tests/generics/t18823.nim | 6 ++++++ 4 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 tests/generics/t18823.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index aba877187b38..55ee0e20de31 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -2040,10 +2040,12 @@ proc skipColon*(n: PNode): PNode = result = n[1] proc findUnresolvedStatic*(n: PNode): PNode = - # n.typ == nil: see issue #14802 if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil: return n - + if n.typ != nil and n.typ.kind == tyTypeDesc: + let t = skipTypes(n.typ, {tyTypeDesc}) + if t.kind == tyGenericParam and t.len == 0: + return n for son in n: let n = son.findUnresolvedStatic if n != nil: return n diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 19aa8be29104..dfa1d2902c18 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -89,7 +89,7 @@ type proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym -proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode +proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable = result = LayeredIdTable() @@ -214,7 +214,7 @@ proc hasValuelessStatics(n: PNode): bool = return true false -proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = +proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode = if n == nil: return result = copyNode(n) if n.typ != nil: @@ -256,8 +256,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode = when false: n = reResolveCallsWithTypedescParams(cl, n) result = if cl.allowMetaTypes: n - else: cl.c.semExpr(cl.c, n) - if not cl.allowMetaTypes: + else: cl.c.semExpr(cl.c, n, {}, expectedType) + if not cl.allowMetaTypes and expectedType != nil: assert result.kind notin nkCallKinds else: if n.len > 0: @@ -694,12 +694,13 @@ proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; result.owner = owner proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; - owner: PSym, allowMetaTypes = false): PNode = + owner: PSym, allowMetaTypes = false, + fromStaticExpr = false, expectedType: PType = nil): PNode = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, n.info, owner) cl.allowMetaTypes = allowMetaTypes pushInfoContext(p.config, n.info) - result = replaceTypeVarsN(cl, n) + result = replaceTypeVarsN(cl, n, expectedType = expectedType) popInfoContext(p.config) when false: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9bf47df70079..462f5d0d1ad9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -821,7 +821,8 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType = result = t proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, - allowUnresolved = false): PNode = + allowUnresolved = false, + expectedType: PType = nil): PNode = # Consider this example: # type Value[N: static[int]] = object # proc foo[N](a: Value[N], r: range[0..(N-1)]) @@ -1179,9 +1180,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if result notin {isNone, isGeneric}: # resolve any late-bound static expressions # that may appear in the range: + let expectedType = base(f) for i in 0..1: if f.n[i].kind == nkStaticExpr: - f.n[i] = tryResolvingStaticExpr(c, f.n[i]) + let r = tryResolvingStaticExpr(c, f.n[i], expectedType = expectedType) + if r != nil: + f.n[i] = r result = typeRangeRel(f, a) else: let f = skipTypes(f, {tyRange}) diff --git a/tests/generics/t18823.nim b/tests/generics/t18823.nim new file mode 100644 index 000000000000..94c79aebe94a --- /dev/null +++ b/tests/generics/t18823.nim @@ -0,0 +1,6 @@ +type BitsRange[T] = range[0..sizeof(T)*8-1] + +proc bar[T](a: T; b: BitsRange[T]) = + discard + +bar(1, 2.Natural) From 4c6be40b340e3ce70b3ed2207db276cb9c661dbe Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 8 Aug 2023 16:08:16 +0800 Subject: [PATCH 134/347] modernize compiler/filter_tmpl.nim (#22407) --- compiler/filter_tmpl.nim | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 84ed9916450c..d04388b96a0c 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -201,17 +201,15 @@ proc parseLine(p: var TTmplParser) = proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream = - var p: TTmplParser - p.config = conf - p.info = newLineInfo(conf, filename, 0, 0) - p.outp = llStreamOpen("") - p.inp = stdin - p.subsChar = charArg(conf, call, "subschar", 1, '$') - p.nimDirective = charArg(conf, call, "metachar", 2, '#') - p.emit = strArg(conf, call, "emit", 3, "result.add") - p.conc = strArg(conf, call, "conc", 4, " & ") - p.toStr = strArg(conf, call, "tostring", 5, "$") - p.x = newStringOfCap(120) + var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0), + outp: llStreamOpen(""), inp: stdin, + subsChar: charArg(conf, call, "subschar", 1, '$'), + nimDirective: charArg(conf, call, "metachar", 2, '#'), + emit: strArg(conf, call, "emit", 3, "result.add"), + conc: strArg(conf, call, "conc", 4, " & "), + toStr: strArg(conf, call, "tostring", 5, "$"), + x: newStringOfCap(120) + ) # do not process the first line which contains the directive: if llStreamReadLine(p.inp, p.x): inc p.info.line From bf5d173bc65aea071e5ffdf593f6b8797b56816d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 8 Aug 2023 17:53:21 +0800 Subject: [PATCH 135/347] fixes LineTooLong hints on old compilers (#22412) * fixes LineTooLong hints on old compilers * fixes config/nim.cfg --- compiler/condsyms.nim | 1 + compiler/nim.cfg | 1 + config/nim.cfg | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 58b89d933015..638cb5c1efa6 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -160,3 +160,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasEnsureMove") defineSymbol("nimUseStrictDefs") + defineSymbol("nimHasNolineTooLong") diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 4c55a04cbc13..04005368512c 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -50,3 +50,4 @@ define:useStdoutAsStdmsg warningAsError[Uninit]:on warningAsError[ProveInit]:on @end + diff --git a/config/nim.cfg b/config/nim.cfg index 1470de78056a..7a2d5c76efe5 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -14,6 +14,10 @@ cc = gcc # additional options always passed to the compiler: --parallel_build: "0" # 0 to auto-detect number of processors +@if not nimHasNolineTooLong: + hint[LineTooLong]=off +@end + @if nimHasAmbiguousEnumHint: # not needed if hint is a style check hint[AmbiguousEnum]=off From 10a6e4c236b8d4d609a07b29ad5b7b4e517cd367 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 8 Aug 2023 17:55:18 +0800 Subject: [PATCH 136/347] clean up `gc:arc` or `gc:orc` in docs and in error messages (#22408) * clean up gc:arc/orc in docs * in error messages --- compiler/ccgexprs.nim | 2 +- lib/std/tasks.nim | 4 ++-- lib/system.nim | 4 ++-- lib/system/arc.nim | 6 +++--- lib/system/orc.nim | 10 +++++----- lib/system/osalloc.nim | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index cc0d0465c5ed..f1de6f757f5e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2621,7 +2621,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDeepCopy: if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions: localError(p.config, e.info, - "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on") + "for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on") var a, b: TLoc = default(TLoc) let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 923cb2e99459..6a4b2bb6d8cd 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -111,7 +111,7 @@ template addAllNode(assignParam: NimNode, procParam: NimNode) = macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task = ## Converts the call and its arguments to `Task`. - runnableExamples("--gc:orc"): + runnableExamples: proc hello(a: int) = echo a let b = toTask hello(13) @@ -259,7 +259,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC when defined(nimTasksDebug): echo result.repr -runnableExamples("--gc:orc"): +runnableExamples: block: var num = 0 proc hello(a: int) = inc num, a diff --git a/lib/system.nim b/lib/system.nim index 1bf1c5ccb406..3076fe2fdae7 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2565,7 +2565,7 @@ when hasAlloc and notJSnotNims: ## This is also used by the code generator ## for the implementation of `spawn`. ## - ## For `--gc:arc` or `--gc:orc` deepcopy support has to be enabled + ## For `--mm:arc` or `--mm:orc` deepcopy support has to be enabled ## via `--deepcopy:on`. discard @@ -2811,7 +2811,7 @@ when notJSnotNims and not defined(nimSeqsV2): ## String literals (e.g. "abc", etc) in the ARC/ORC mode are "copy on write", ## therefore you should call `prepareMutation` before modifying the strings ## via `addr`. - runnableExamples("--gc:arc"): + runnableExamples: var x = "abc" var y = "defgh" prepareMutation(y) # without this, you may get a `SIGBUS` or `SIGSEGV` diff --git a/lib/system/arc.nim b/lib/system/arc.nim index b264cbeddce6..0624bd4e3af6 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -223,15 +223,15 @@ proc GC_ref*[T](x: ref T) = when not defined(gcOrc): template GC_fullCollect* = - ## Forces a full garbage collection pass. With `--gc:arc` a nop. + ## Forces a full garbage collection pass. With `--mm:arc` a nop. discard template setupForeignThreadGc* = - ## With `--gc:arc` a nop. + ## With `--mm:arc` a nop. discard template tearDownForeignThreadGc* = - ## With `--gc:arc` a nop. + ## With `--mm:arc` a nop. discard proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} = diff --git a/lib/system/orc.nim b/lib/system/orc.nim index b7b98c3404a6..335d49d0fa8b 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -424,13 +424,13 @@ proc GC_runOrc* = orcAssert roots.len == 0, "roots not empty!" proc GC_enableOrc*() = - ## Enables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` + ## Enables the cycle collector subsystem of `--mm:orc`. This is a `--mm:orc` ## specific API. Check with `when defined(gcOrc)` for its existence. when not defined(nimStressOrc): rootsThreshold = 0 proc GC_disableOrc*() = - ## Disables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` + ## Disables the cycle collector subsystem of `--mm:orc`. This is a `--mm:orc` ## specific API. Check with `when defined(gcOrc)` for its existence. when not defined(nimStressOrc): rootsThreshold = high(int) @@ -441,16 +441,16 @@ proc GC_partialCollect*(limit: int) = partialCollect(limit) proc GC_fullCollect* = - ## Forces a full garbage collection pass. With `--gc:orc` triggers the cycle + ## Forces a full garbage collection pass. With `--mm:orc` triggers the cycle ## collector. This is an alias for `GC_runOrc`. collectCycles() proc GC_enableMarkAndSweep*() = - ## For `--gc:orc` an alias for `GC_enableOrc`. + ## For `--mm:orc` an alias for `GC_enableOrc`. GC_enableOrc() proc GC_disableMarkAndSweep*() = - ## For `--gc:orc` an alias for `GC_disableOrc`. + ## For `--mm:orc` an alias for `GC_disableOrc`. GC_disableOrc() const diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 201be8540ee9..5509d0070c34 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -30,7 +30,7 @@ const doNotUnmap = not (defined(amd64) or defined(i386)) or when defined(nimAllocPagesViaMalloc): when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc): - {.error: "-d:nimAllocPagesViaMalloc is only supported with --gc:arc or --gc:orc".} + {.error: "-d:nimAllocPagesViaMalloc is only supported with --mm:arc or --mm:atomicArc or --mm:orc".} proc osTryAllocPages(size: int): pointer {.inline.} = let base = c_malloc(csize_t size + PageSize - 1 + sizeof(uint32)) From 73e661d01bdde815a5f388b960cd4d0593f7accc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 8 Aug 2023 21:12:54 +0800 Subject: [PATCH 137/347] modernize compiler/reorder, which exposes yet another strictdefs bug (#22415) ```nim {.experimental: "strictdefs".} type NodeKind = enum nkImportStmt nkStmtList nkNone PNode = ref object kind: NodeKind proc hasImportStmt(n: PNode): bool = # Checks if the node is an import statement or # i it contains one case n.kind of nkImportStmt: return true of nkStmtList: if false: return true else: result = false var n = PNode() echo hasImportStmt(n) ``` It compiles without warnings, but shouldn't. As a contrast, ```nim {.experimental: "strictdefs".} type NodeKind = enum nkImportStmt nkStmtList nkNone PNode = ref object kind: NodeKind proc hasImportStmt(n: PNode): bool = # Checks if the node is an import statement or # i it contains one case n.kind of nkImportStmt: result = true of nkStmtList: if false: return true else: result = false var n = PNode() echo hasImportStmt(n) ``` This gives a proper warning. --- compiler/reorder.nim | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index f43ddc2031da..a96841bcad66 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -25,17 +25,11 @@ when defined(nimDebugReorder): var idNames = newTable[int, string]() proc newDepN(id: int, pnode: PNode): DepN = - new(result) - result.id = id - result.pnode = pnode - result.idx = -1 - result.lowLink = -1 - result.onStack = false - result.kids = @[] - result.hAQ = -1 - result.hIS = -1 - result.hB = -1 - result.hCmd = -1 + result = DepN(id: id, pnode: pnode, idx: -1, + lowLink: -1, onStack: false, + kids: @[], hAQ: -1, hIS: -1, + hB: -1, hCmd: -1 + ) when defined(nimDebugReorder): result.expls = @[] @@ -114,7 +108,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev # XXX: for callables, this technically adds the return type dep before args for i in 0.. Date: Wed, 9 Aug 2023 08:18:47 +0800 Subject: [PATCH 138/347] modernize lineinfos; it seems that array access hinders strict def analysis like field access (#22420) modernize lineinfos; array access hinders strict def analysis like field access A bug ? ```nim proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept} result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance} result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC} ``` --- compiler/lineinfos.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 6ebf3b53812d..12495aa22fd4 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -## This module contains the ``TMsgKind`` enum as well as the -## ``TLineInfo`` object. +## This module contains the `TMsgKind` enum as well as the +## `TLineInfo` object. import ropes, tables, pathutils, hashes @@ -248,6 +248,7 @@ type TNoteKinds* = set[TNoteKind] proc computeNotesVerbosity(): array[0..3, TNoteKinds] = + result = default(array[0..3, TNoteKinds]) result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept} result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt} result[1] = result[2] - {warnProveField, warnProveIndex, @@ -341,9 +342,8 @@ type proc initMsgConfig*(): MsgConfig = - result.msgContext = @[] - result.lastError = unknownLineInfo - result.filenameToIndexTbl = initTable[string, FileIndex]() - result.fileInfos = @[] - result.errorOutputs = {eStdOut, eStdErr} + result = MsgConfig(msgContext: @[], lastError: unknownLineInfo, + filenameToIndexTbl: initTable[string, FileIndex](), + fileInfos: @[], errorOutputs: {eStdOut, eStdErr} + ) result.filenameToIndexTbl["???"] = FileIndex(-1) From 3aaef9e4cf014d5df65a686efe6b8bd99d3ea465 Mon Sep 17 00:00:00 2001 From: metagn Date: Wed, 9 Aug 2023 07:12:14 +0300 Subject: [PATCH 139/347] block ambiguous type conversion dotcalls in generics (#22375) fixes #22373 --- compiler/semgnrc.nim | 11 +++++++++++ tests/generics/m22373a.nim | 7 +++++++ tests/generics/m22373b.nim | 18 ++++++++++++++++++ tests/generics/t22373.nim | 16 ++++++++++++++++ tests/generics/timports.nim | 5 +++++ 5 files changed, 57 insertions(+) create mode 100644 tests/generics/m22373a.nim create mode 100644 tests/generics/m22373b.nim create mode 100644 tests/generics/t22373.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 43b8d4bac451..c8eda9c37db4 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -168,6 +168,17 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.isMixedIn: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: + if s.kind == skType and candidates.len > 1: + var ambig = false + let s2 = searchInScopes(c, ident, ambig) + if ambig: + # this is a type conversion like a.T where T is ambiguous with + # other types or routines + # in regular code, this never considers a type conversion and + # skips to routine overloading + # so symchoices are used which behave similarly with type symbols + result = newDot(result, symChoice(c, n, s, scForceOpen)) + return let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true) result = newDot(result, syms) diff --git a/tests/generics/m22373a.nim b/tests/generics/m22373a.nim new file mode 100644 index 000000000000..28e087ca617a --- /dev/null +++ b/tests/generics/m22373a.nim @@ -0,0 +1,7 @@ +# module a for t22373 + +# original: +type LightClientHeader* = object + +# simplified: +type TypeOrTemplate* = object diff --git a/tests/generics/m22373b.nim b/tests/generics/m22373b.nim new file mode 100644 index 000000000000..67ee4211be0e --- /dev/null +++ b/tests/generics/m22373b.nim @@ -0,0 +1,18 @@ +# module b for t22373 + +import m22373a + +# original: +type + LightClientDataFork* {.pure.} = enum + None = 0, + Altair = 1 +template LightClientHeader*(kind: static LightClientDataFork): auto = + when kind == LightClientDataFork.Altair: + typedesc[m22373a.LightClientHeader] + else: + static: raiseAssert "Unreachable" + +# simplified: +template TypeOrTemplate*(num: int): untyped = + typedesc[m22373a.TypeOrTemplate] diff --git a/tests/generics/t22373.nim b/tests/generics/t22373.nim new file mode 100644 index 000000000000..ecfaf0f1ba30 --- /dev/null +++ b/tests/generics/t22373.nim @@ -0,0 +1,16 @@ +# issue #22373 + +import m22373a +import m22373b + +# original: +template lazy_header(name: untyped): untyped {.dirty.} = + var `name _ ptr`: ptr[data_fork.LightClientHeader] # this data_fork.Foo part seems required to reproduce +proc createLightClientUpdates(data_fork: static LightClientDataFork) = + lazy_header(attested_header) +createLightClientUpdates(LightClientDataFork.Altair) + +# simplified: +proc generic[T](abc: T) = + var x: abc.TypeOrTemplate +generic(123) diff --git a/tests/generics/timports.nim b/tests/generics/timports.nim index 43f096664ed2..6b71cb6d3b44 100644 --- a/tests/generics/timports.nim +++ b/tests/generics/timports.nim @@ -46,6 +46,11 @@ block tdotlookup: x.set("hello", "world") result = x doAssert abc(5) == 10 + block: # ensure normal call is consistent with dot call + proc T(x: int): float = x.float + proc foo[T](x: int) = + doAssert typeof(T(x)) is typeof(x.T) + foo[uint](123) block tmodule_same_as_proc: # bug #1965 From ce079a8da45c8513b8864f92be4baba40a44fb7b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:33:19 +0800 Subject: [PATCH 140/347] modernize jsgen; clean up some leftovers (#22423) --- compiler/jsgen.nim | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a3df0a2ba30b..1a31547534ee 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -137,17 +137,15 @@ template nested(p, body) = dec p.extraIndent proc newGlobals(): PGlobals = - new(result) - result.forwarded = @[] - result.generatedSyms = initIntSet() - result.typeInfoGenerated = initIntSet() + result = PGlobals(forwarded: @[], + generatedSyms: initIntSet(), + typeInfoGenerated: initIntSet() + ) -proc initCompRes(r: var TCompRes) = - r.address = "" - r.res = "" - r.tmpLoc = "" - r.typ = etyNone - r.kind = resNone +proc initCompRes(): TCompRes = + result = TCompRes(address: "", res: "", + tmpLoc: "", typ: etyNone, kind: resNone + ) proc rdLoc(a: TCompRes): Rope {.inline.} = if a.typ != etyBaseIndex: @@ -350,9 +348,10 @@ proc isSimpleExpr(p: PProc; n: PNode): bool = if n[i].kind notin {nkCommentStmt, nkEmpty}: return false result = isSimpleExpr(p, n.lastSon) else: - result = false if n.isAtom: result = true + else: + result = false proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) @@ -3006,13 +3005,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = proc newModule(g: ModuleGraph; module: PSym): BModule = ## Create a new JS backend module node. - new(result) - result.module = module - result.sigConflicts = initCountTable[SigHash]() if g.backend == nil: g.backend = newGlobals() - result.graph = g - result.config = g.config + result = BModule(module: module, sigConflicts: initCountTable[SigHash](), + graph: g, config: g.config + ) if sfSystemModule in module.flags: PGlobals(g.backend).inSystem = true From 28b2e429ef64503f7a239ee8fc3043c3fe883ec6 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 9 Aug 2023 12:40:17 +0800 Subject: [PATCH 141/347] refactors initSrcGen and initTokRender into returning objects (#22421) --- compiler/docgen.nim | 13 +++---- compiler/renderer.nim | 84 ++++++++++++++++--------------------------- 2 files changed, 36 insertions(+), 61 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4a0ae6fc9b27..6a78d8693293 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -492,9 +492,8 @@ proc externalDep(d: PDoc; module: PSym): string = proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; renderFlags: TRenderFlags = {}; procLink: string) = - var r: TSrcGen = TSrcGen() + var r: TSrcGen = initTokRender(n, renderFlags) var literal = "" - initTokRender(r, n, renderFlags) var kind = tkEof var tokenPos = 0 var procTokenPos = 0 @@ -1030,8 +1029,7 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = genNode = n[miscPos][1] # FIXME: what is index 1? if genNode != nil: var literal = "" - var r: TSrcGen - initTokRender(r, genNode, {renderNoBody, renderNoComments, + var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments, renderNoPragmas, renderNoProcDefs, renderExpandUsing}) var kind = tkEof while true: @@ -1058,9 +1056,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx else: comm.add genRecComment(d, n) - var r: TSrcGen # Obtain the plain rendered string for hyperlink titles. - initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, + var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments, renderNoPragmas, renderNoProcDefs, renderExpandUsing}) while true: getNextTok(r, kind, literal) @@ -1164,11 +1161,11 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): var name = getNameEsc(d, nameNode) comm = genRecComment(d, n) - r: TSrcGen = default(TSrcGen) + r: TSrcGen renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing} if nonExports: renderFlags.incl renderNonExportedFields - initTokRender(r, n, renderFlags) + r = initTokRender(n, renderFlags) result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} if comm != nil: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 2af8d83269ec..e39be78fe964 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -145,19 +145,13 @@ const MaxLineLen = 80 LineCommentColumn = 30 -proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = - g.comStack = @[] - g.tokens = @[] - g.indent = 0 - g.lineLen = 0 - g.pos = 0 - g.idx = 0 - g.buf = "" - g.flags = renderFlags - g.pendingNL = -1 - g.pendingWhitespace = -1 - g.inside = {} - g.config = config +proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen = + result = TSrcGen(comStack: @[], tokens: @[], indent: 0, + lineLen: 0, pos: 0, idx: 0, buf: "", + flags: renderFlags, pendingNL: -1, + pendingWhitespace: -1, inside: {}, + config: config + ) proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym) @@ -630,14 +624,12 @@ type const emptyContext: TContext = (spacing: 0, flags: {}) -proc initContext(c: var TContext) = - c.spacing = 0 - c.flags = {} +proc initContext(): TContext = + result = (spacing: 0, flags: {}) proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) = - var c: TContext = default(TContext) - initContext(c) + var c: TContext = initContext() gsub(g, n, c, fromStmtList = fromStmtList) proc hasCom(n: PNode): bool = @@ -767,9 +759,8 @@ proc gcond(g: var TSrcGen, n: PNode) = put(g, tkParRi, ")") proc gif(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() gcond(g, n[0][0]) - initContext(c) putWithSpace(g, tkColon, ":") if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) @@ -780,20 +771,18 @@ proc gif(g: var TSrcGen, n: PNode) = gsub(g, n[i], c) proc gwhile(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() putWithSpace(g, tkWhile, "while") gcond(g, n[0]) putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n[1], c) proc gpattern(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() put(g, tkCurlyLe, "{") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -801,20 +790,18 @@ proc gpattern(g: var TSrcGen, n: PNode) = put(g, tkCurlyRi, "}") proc gpragmaBlock(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() gsub(g, n[0]) putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n[1], c) proc gtry(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() put(g, tkTry, "try") putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -822,9 +809,8 @@ proc gtry(g: var TSrcGen, n: PNode) = gsons(g, n, c, 1) proc gfor(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() putWithSpace(g, tkFor, "for") - initContext(c) if longMode(g, n) or (lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) @@ -837,8 +823,7 @@ proc gfor(g: var TSrcGen, n: PNode) = gstmts(g, n[^1], c) proc gcase(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) - initContext(c) + var c: TContext = initContext() if n.len == 0: return var last = if n[^1].kind == nkElse: -2 else: -1 if longMode(g, n, 0, last): incl(c.flags, rfLongMode) @@ -848,7 +833,7 @@ proc gcase(g: var TSrcGen, n: PNode) = optNL(g) gsons(g, n, c, 1, last) if last == - 2: - initContext(c) + c = initContext() if longMode(g, n[^1]): incl(c.flags, rfLongMode) gsub(g, n[^1], c) @@ -858,7 +843,7 @@ proc genSymSuffix(result: var string, s: PSym) {.inline.} = result.addInt s.id proc gproc(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() if n[namePos].kind == nkSym: let s = n[namePos].sym var ret = renderDefinitionName(s) @@ -885,7 +870,7 @@ proc gproc(g: var TSrcGen, n: PNode) = indentNL(g) gcoms(g) dedent(g) - initContext(c) + c = initContext() gstmts(g, n[bodyPos], c) putNL(g) else: @@ -894,8 +879,7 @@ proc gproc(g: var TSrcGen, n: PNode) = dedent(g) proc gTypeClassTy(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) - initContext(c) + var c: TContext = initContext() putWithSpace(g, tkConcept, "concept") gsons(g, n[0], c) # arglist gsub(g, n[1]) # pragmas @@ -914,8 +898,7 @@ proc gblock(g: var TSrcGen, n: PNode) = if n.len == 0: return - var c: TContext = default(TContext) - initContext(c) + var c: TContext = initContext() if n[0].kind != nkEmpty: putWithSpace(g, tkBlock, "block") @@ -935,10 +918,9 @@ proc gblock(g: var TSrcGen, n: PNode) = gstmts(g, n[1], c) proc gstaticStmt(g: var TSrcGen, n: PNode) = - var c: TContext = default(TContext) + var c: TContext = initContext() putWithSpace(g, tkStatic, "static") putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -1631,7 +1613,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkTypeSection: gsection(g, n, emptyContext, tkType, "type") of nkConstSection: - initContext(a) + a = initContext() incl(a.flags, rfInConstExpr) gsection(g, n, a, tkConst, "const") of nkVarSection, nkLetSection, nkUsingStmt: @@ -1799,13 +1781,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n, 0) put(g, tkParRi, ")") of nkGotoState: - var c: TContext = default(TContext) - initContext c + var c: TContext = initContext() putWithSpace g, tkSymbol, "goto" gsons(g, n, c) of nkState: - var c: TContext = default(TContext) - initContext c + var c: TContext = initContext() putWithSpace g, tkSymbol, "state" gsub(g, n[0], c) putWithSpace(g, tkColon, ":") @@ -1829,8 +1809,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = if n == nil: return "" - var g: TSrcGen = default(TSrcGen) - initSrcGen(g, renderFlags, newPartialConfigRef()) + var g: TSrcGen = initSrcGen(renderFlags, newPartialConfigRef()) # do not indent the initial statement list so that # writeFile("file.nim", repr n) # produces working Nim code: @@ -1848,8 +1827,7 @@ proc renderModule*(n: PNode, outfile: string, conf: ConfigRef = nil) = var f: File = default(File) - g: TSrcGen - initSrcGen(g, renderFlags, conf) + g: TSrcGen = initSrcGen(renderFlags, conf) g.fid = fid for i in 0.. Date: Wed, 9 Aug 2023 13:18:50 +0800 Subject: [PATCH 142/347] make the name of procs consistent with the name forwards (#22424) It seems that `--stylecheck:error` acts up when the name forwards is involved. ```nim proc thisOne*(x: var int) proc thisone(x: var int) = x = 1 ``` It cannot understand this at all. --- compiler/astalgo.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index d0aec085f565..621bd23808b9 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -109,7 +109,7 @@ type data*: TIIPairSeq -proc initIiTable*(x: var TIITable) +proc initIITable*(x: var TIITable) proc iiTableGet*(t: TIITable, key: int): int proc iiTablePut*(t: var TIITable, key, val: int) From 989da75b84e15a6d585e8b03d2bcd0c42a90c2fb Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 9 Aug 2023 15:43:39 +0800 Subject: [PATCH 143/347] fix #20891 Illegal capture error of env its self (#22414) * fix #20891 Illegal capture error of env its self * fix innerClosure too earlier, make condition shorter --- compiler/ast.nim | 6 ++++++ compiler/lambdalifting.nim | 10 +++++++--- tests/iter/t20891.nim | 28 ++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 tests/iter/t20891.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 55ee0e20de31..fc3f523787fe 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -2083,6 +2083,12 @@ proc isClosureIterator*(typ: PType): bool {.inline.} = proc isClosure*(typ: PType): bool {.inline.} = typ.kind == tyProc and typ.callConv == ccClosure +proc isNimcall*(s: PSym): bool {.inline.} = + s.typ.callConv == ccNimCall + +proc isExplicitCallConv*(s: PSym): bool {.inline.} = + tfExplicitCallConv in s.typ.flags + proc isSinkParam*(s: PSym): bool {.inline.} = s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ac4c160f9398..37c913fe2e5d 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -297,17 +297,19 @@ proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = let s = n.sym + let isEnv = s.name.id == getIdent(g.cache, ":env").id if illegalCapture(s): localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & " Consider using a which can be captured.") % [s.name.s, typeToString(s.typ), g.config$s.info]) - elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): + elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, $owner.typ.callConv]) incl(owner.typ.flags, tfCapturesEnv) - owner.typ.callConv = ccClosure + if not isEnv: + owner.typ.callConv = ccClosure type DetectionPass = object @@ -448,6 +450,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = let body = transformBody(c.graph, c.idgen, s, useCache) detectCapturedVars(body, s, c) let ow = s.skipGenericOwner + let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator + let interested = interestingVar(s) if ow == owner: if owner.isIterator: c.somethingToDo = true @@ -462,7 +466,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = else: discard addField(obj, s, c.graph.cache, c.idgen) # direct or indirect dependency: - elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s): + elif innerClosure or interested: discard """ proc outer() = var x: int diff --git a/tests/iter/t20891.nim b/tests/iter/t20891.nim new file mode 100644 index 000000000000..34deec41bd77 --- /dev/null +++ b/tests/iter/t20891.nim @@ -0,0 +1,28 @@ +import macros, tables + +var mapping {.compileTime.}: Table[string, NimNode] + +macro register(a: static[string], b: typed): untyped = + mapping[a] = b + +macro getPtr(a: static[string]): untyped = + result = mapping[a] + +proc foo() = + iterator it() {.closure.} = + discard + proc getIterPtr(): pointer {.nimcall.} = + rawProc(it) + register("foo", getIterPtr()) + discard getIterPtr() # Comment either this to make it work +foo() # or this + +proc bar() = + iterator it() {.closure.} = + discard getPtr("foo") # Or this + discard + proc getIterPtr(): pointer {.nimcall.} = + rawProc(it) + register("bar", getIterPtr()) + discard getIterPtr() +bar() From 5334dc921fc177bf6253256dcd579baf244f1ad8 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 9 Aug 2023 18:43:17 +0800 Subject: [PATCH 144/347] fixes #22419; async/closure environment does not align local variables (#22425) * fixes #22419; async/closure environment does not align local variables * Apply suggestions from code review * Update tests/align/talign.nim Co-authored-by: Jacek Sieka * apply code review * update tests --------- Co-authored-by: Jacek Sieka --- compiler/lowerings.nim | 3 +++ tests/align/talign.nim | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index d70c713a1512..a083b9195315 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -237,6 +237,9 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) let t = skipIntLit(s.typ, idgen) field.typ = t + if s.kind in {skLet, skVar, skField, skForVar}: + #field.bitsize = s.bitsize + field.alignment = s.alignment assert t.kind != tyTyped propagateToOwner(obj, t) field.position = obj.n.len diff --git a/tests/align/talign.nim b/tests/align/talign.nim index 3b8f6b4dfbbc..08373ee4977c 100644 --- a/tests/align/talign.nim +++ b/tests/align/talign.nim @@ -51,3 +51,19 @@ type Bug[T] = object var bug: Bug[int] doAssert sizeof(bug) == 128, "Oops my size is " & $sizeof(bug) # 16 + + +block: # bug #22419 + type + ValidatorPubKey = object + blob: array[96, byte] + + proc f(): auto = + return iterator() = + var pad: int8 = 0 + var y {.align: 16.}: ValidatorPubKey + let value = cast[uint64](addr y) + doAssert value mod 16 == 0 + + f()() + From d53a89e4539a33d9f7cf8416155d7cb700628872 Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 9 Aug 2023 18:45:43 +0800 Subject: [PATCH 145/347] fix #12938 index type of array in type section without static (#20529) * fix #12938 nim compiler assertion fail when literal integer is passed as template argument for array size * use new flag tfImplicitStatic * fix * fix #14193 * correct tfUnresolved add condition * clean test --- compiler/ast.nim | 1 + compiler/semtypes.nim | 57 ++++++++++++++++++++++++--------------- tests/generics/t12938.nim | 9 +++++++ tests/generics/t14193.nim | 6 +++++ 4 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 tests/generics/t12938.nim create mode 100644 tests/generics/t14193.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index fc3f523787fe..d62b563682eb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -591,6 +591,7 @@ type tfEffectSystemWorkaround tfIsOutParam tfSendable + tfImplicitStatic TTypeFlags* = set[TTypeFlag] diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 06be954e6db4..5e72996ac15a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -335,6 +335,14 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "range") result = newOrPrevType(tyError, prev, c) +proc semArrayIndexConst(c: PContext, e: PNode, info: TLineInfo): PType = + let x = semConstExpr(c, e) + if x.kind in {nkIntLit..nkUInt64Lit}: + result = makeRangeType(c, 0, x.intVal-1, info, + x.typ.skipTypes({tyTypeDesc})) + else: + result = x.typ.skipTypes({tyTypeDesc}) + proc semArrayIndex(c: PContext, n: PNode): PType = if isRange(n): result = semRangeAux(c, n, nil) @@ -351,14 +359,18 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, n.info, "Array length can't be negative, but was " & $e.intVal) result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) - elif e.kind == nkSym and e.typ.kind == tyStatic: - if e.sym.ast != nil: - return semArrayIndex(c, e.sym.ast) - if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon): - let info = if n.safeLen > 1: n[1].info else: n.info - localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) - result = makeRangeWithStaticExpr(c, e) - if c.inGenericContext > 0: result.flags.incl tfUnresolved + elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc) : + if e.typ.kind == tyStatic: + if e.sym.ast != nil: + return semArrayIndex(c, e.sym.ast) + if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon): + let info = if n.safeLen > 1: n[1].info else: n.info + localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) + result = makeRangeWithStaticExpr(c, e) + if c.inGenericContext > 0: result.flags.incl tfUnresolved + else: + result = e.typ.skipTypes({tyTypeDesc}) + result.flags.incl tfImplicitStatic elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})): localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) @@ -371,12 +383,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = elif e.kind == nkIdent: result = e.typ.skipTypes({tyTypeDesc}) else: - let x = semConstExpr(c, e) - if x.kind in {nkIntLit..nkUInt64Lit}: - result = makeRangeType(c, 0, x.intVal-1, n.info, - x.typ.skipTypes({tyTypeDesc})) - else: - result = x.typ.skipTypes({tyTypeDesc}) + result = semArrayIndexConst(c, e, n.info) #localError(c.config, n[1].info, errConstExprExpected) proc semArray(c: PContext, n: PNode, prev: PType): PType = @@ -1504,20 +1511,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = var t = s.typ.skipTypes({tyAlias}) if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: t = t.base - result = newOrPrevType(tyGenericInvocation, prev, c) addSonSkipIntLit(result, t, c.idgen) - template addToResult(typ) = + template addToResult(typ, skip) = + if typ.isNil: internalAssert c.config, false rawAddSon(result, typ) - else: addSonSkipIntLit(result, typ, c.idgen) + else: + if skip: + addSonSkipIntLit(result, typ, c.idgen) + else: + rawAddSon(result, makeRangeWithStaticExpr(c, typ.n)) if t.kind == tyForward: for i in 1..= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ): + skip = false + addToResult(typ, skip) if isConcrete: if s.ast == nil and s.typ.kind != tyCompositeTypeClass: diff --git a/tests/generics/t12938.nim b/tests/generics/t12938.nim new file mode 100644 index 000000000000..e09d65c7ae4a --- /dev/null +++ b/tests/generics/t12938.nim @@ -0,0 +1,9 @@ +type + ExampleArray[Size, T] = array[Size, T] + +var integerArray: ExampleArray[32, int] # Compiler crash! +doAssert integerArray.len == 32 + +const Size = 2 +var integerArray2: ExampleArray[Size, int] +doAssert integerArray2.len == 2 diff --git a/tests/generics/t14193.nim b/tests/generics/t14193.nim new file mode 100644 index 000000000000..213b1a8e6e6d --- /dev/null +++ b/tests/generics/t14193.nim @@ -0,0 +1,6 @@ +type + Task*[N: int] = object + env*: array[N, byte] + +var task14193: Task[20] +doAssert task14193.env.len == 20 From 5ec81d076b2ddd11655ec1e90938ea08c907ab0b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 9 Aug 2023 19:49:30 +0800 Subject: [PATCH 146/347] fixes cascades of out parameters, which produces wrong ProveInit warnings (#22413) --- compiler/sempass2.nim | 14 ++++++++++---- tests/init/toutparams.nim | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 tests/init/toutparams.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index af283af3038a..c12cc6cb3dd9 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -846,7 +846,9 @@ proc trackCall(tracked: PEffects; n: PNode) = if n.typ != nil: if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray: createTypeBoundOps(tracked, n.typ, n.info) - if getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil: + + let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil + if notConstExpr: if a.kind == nkCast and a[1].typ.kind == tyProc: a = a[1] # XXX: in rare situations, templates and macros will reach here after @@ -905,9 +907,6 @@ proc trackCall(tracked: PEffects; n: PNode) = optStaticBoundsCheck in tracked.currOptions: checkBounds(tracked, n[1], n[2]) - if a.kind != nkSym or a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst}: - for i in 0.. 0 and a.sym.name.s[0] == '=' and tracked.owner.kind != skMacro: @@ -943,6 +942,13 @@ proc trackCall(tracked: PEffects; n: PNode) = tracked.hasSideEffect = true else: discard + if notConstExpr and (a.kind != nkSym or + a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst} + ): + # tracked after out analysis + for i in 0.. Date: Wed, 9 Aug 2023 23:17:08 +0800 Subject: [PATCH 147/347] Fix #5780 (#22428) * fix #5780 --- compiler/sigmatch.nim | 2 +- tests/statictypes/t5780.nim | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/statictypes/t5780.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 462f5d0d1ad9..c0b74f8214ff 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1225,7 +1225,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyArray: var fRange = f[0] var aRange = a[0] - if fRange.kind == tyGenericParam: + if fRange.kind in {tyGenericParam, tyAnything}: var prev = PType(idTableGet(c.bindings, fRange)) if prev == nil: put(c, fRange, a[0]) diff --git a/tests/statictypes/t5780.nim b/tests/statictypes/t5780.nim new file mode 100644 index 000000000000..85548aaadd32 --- /dev/null +++ b/tests/statictypes/t5780.nim @@ -0,0 +1,3 @@ +type StringArray[N:int] = array[N, string] +let a = ["one", "two"] +doAssert a is StringArray From 91c32218559924ef4f74302310e9195773183f79 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 10 Aug 2023 02:57:13 +0800 Subject: [PATCH 148/347] simplify isAtom condition (#22430) --- compiler/ccgexprs.nim | 5 +---- compiler/jsgen.nim | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f1de6f757f5e..46a353dce004 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1152,10 +1152,7 @@ proc isSimpleExpr(n: PNode): bool = if n[i].kind notin {nkCommentStmt, nkEmpty}: return false result = isSimpleExpr(n.lastSon) else: - if n.isAtom: - result = true - else: - result = false + result = n.isAtom proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # how to generate code? diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 1a31547534ee..8659d511bf7a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -348,10 +348,7 @@ proc isSimpleExpr(p: PProc; n: PNode): bool = if n[i].kind notin {nkCommentStmt, nkEmpty}: return false result = isSimpleExpr(p, n.lastSon) else: - if n.isAtom: - result = true - else: - result = false + result = n.isAtom proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) From 6ec1c80779831875b2552d6ba2d613503b53a012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Wed, 9 Aug 2023 19:57:52 +0100 Subject: [PATCH 149/347] makes asmnostackframe work with cpp member #22411 (#22429) --- compiler/cgen.nim | 7 ++++--- tests/cpp/tvirtual.nim | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e21d85a07244..af30f546ecd0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1142,7 +1142,8 @@ proc isNoReturn(m: BModule; s: PSym): bool {.inline.} = proc genProcAux*(m: BModule, prc: PSym) = var p = newProc(prc, m) var header = newRopeAppender() - if m.config.backend == backendCpp and sfCppMember * prc.flags != {}: + let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {} + if isCppMember: genMemberProcHeader(m, prc, header) else: genProcHeader(m, prc, header) @@ -1205,10 +1206,10 @@ proc genProcAux*(m: BModule, prc: PSym) = var generatedProc: Rope = "" generatedProc.genCLineDir prc.info, m.config if isNoReturn(p.module, prc): - if hasDeclspec in extccomp.CC[p.config.cCompiler].props: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember: header = "__declspec(noreturn) " & header if sfPure in prc.flags: - if hasDeclspec in extccomp.CC[p.config.cCompiler].props: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember: header = "__declspec(naked) " & header generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N", [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) diff --git a/tests/cpp/tvirtual.nim b/tests/cpp/tvirtual.nim index 7acec21baafc..fb792380b309 100644 --- a/tests/cpp/tvirtual.nim +++ b/tests/cpp/tvirtual.nim @@ -79,3 +79,40 @@ type Doo = object proc naiveMember(x: Doo): int {. virtual .} = 2 discard naiveMember(Doo()) +#asmnostackframe works with virtual +{.emit:"""/*TYPESECTION*/ + template + struct Box { + T* first; + + Box(int x){ + first = new T(x); + }; + }; + struct Inner { + int val; + //Coo() = default; + Inner(int x){ + val = x; + }; + }; + struct Base { + virtual Box test() = 0; + }; +""".} + +type + Inner {.importcpp.} = object + Base {.importcpp, inheritable.} = object + Child = object of Base + Box[T] {.importcpp, inheritable.} = object + first: T + +proc makeBox[T](x:int32): Box[T] {.importcpp:"Box<'0>(@)", constructor.} + +proc test(self: Child): Box[Inner] {.virtual, asmnostackframe.} = + let res {.exportc.} = makeBox[Inner](100) + {.emit:"return res;".} + + +discard Child().test() \ No newline at end of file From fa58d23080dad13283cd180260b14cf8c57ab501 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 10 Aug 2023 11:29:42 +0800 Subject: [PATCH 150/347] modernize sempass2; `initEffects` now returns `TEffects` (#22435) --- compiler/sempass2.nim | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index c12cc6cb3dd9..0aa8080597ee 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1433,27 +1433,21 @@ proc rawInitEffects(g: ModuleGraph; effects: PNode) = effects[ensuresEffects] = g.emptyNode effects[pragmasEffects] = g.emptyNode -proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = +proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects = rawInitEffects(g, effects) - t.exc = effects[exceptionEffects] - t.tags = effects[tagEffects] - t.forbids = effects[forbiddenEffects] - t.owner = s - t.ownerModule = s.getModule - t.init = @[] - t.guards.s = @[] - t.guards.g = g + result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects], + forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule, + init: @[], locked: @[], graph: g, config: g.config, c: c, + currentBlock: 1 + ) + result.guards.s = @[] + result.guards.g = g when defined(drnim): - t.currOptions = g.config.options + s.options - {optStaticBoundsCheck} + result.currOptions = g.config.options + s.options - {optStaticBoundsCheck} else: - t.currOptions = g.config.options + s.options - t.guards.beSmart = optStaticBoundsCheck in t.currOptions - t.locked = @[] - t.graph = g - t.config = g.config - t.c = c - t.currentBlock = 1 + result.currOptions = g.config.options + s.options + result.guards.beSmart = optStaticBoundsCheck in result.currOptions proc hasRealBody(s: PSym): bool = ## also handles importc procs with runnableExamples, which requires `=`, @@ -1474,8 +1468,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = var inferredEffects = newNodeI(nkEffectList, s.info) - var t: TEffects = default(TEffects) - initEffects(g, inferredEffects, s, t, c) + var t: TEffects = initEffects(g, inferredEffects, s, c) rawInitEffects g, effects if not isEmptyType(s.typ[0]) and @@ -1586,8 +1579,7 @@ proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) = return let g = c.graph var effects = newNodeI(nkEffectList, n.info) - var t: TEffects - initEffects(g, effects, module, t, c) + var t: TEffects = initEffects(g, effects, module, c) t.isTopLevel = isTopLevel track(t, n) when defined(drnim): From baf350493b08d5e1ee25f61b7d7eff33c3499487 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Thu, 10 Aug 2023 07:56:09 +0200 Subject: [PATCH 151/347] Fix #21760 (#22422) * Remove call-specific replaceTypeVarsN * Run for all call kinds and ignore typedesc * Testcase --------- Co-authored-by: SirOlaf <> --- compiler/seminst.nim | 4 ++-- tests/generics/t21760.nim | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/generics/t21760.nim diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 22d28999d8e8..0dc3e3cfc90c 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -275,9 +275,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree - if def.kind == nkCall: + if def.kind in nkCallKinds: for i in 1.. Date: Thu, 10 Aug 2023 13:57:34 +0800 Subject: [PATCH 152/347] `initLocExpr` and friends now return `TLoc` (#22434) `initLocExpr` and friends now return TLoc --- compiler/ccgcalls.nim | 58 +++--- compiler/ccgexprs.nim | 399 +++++++++++++++++------------------------- compiler/ccgstmts.nim | 83 ++++----- compiler/cgen.nim | 35 ++-- 4 files changed, 234 insertions(+), 341 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index dca581fadc1f..b446e83607a7 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -118,8 +118,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, getTempCpp(p, typ[0], d, pl) else: if d.k == locNone: getTemp(p, typ[0], d) - var list: TLoc - initLoc(list, locCall, d.lode, OnUnknown) + var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) @@ -127,16 +126,14 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, elif isHarmlessStore(p, canRaise, d): if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: - var list: TLoc - initLoc(list, locCall, d.lode, OnUnknown) + var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) else: var tmp: TLoc getTemp(p, typ[0], tmp, needsInit=true) - var list: TLoc = default(TLoc) - initLoc(list, locCall, d.lode, OnUnknown) + var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, tmp, list, {}) # no need for deep copying if canRaise: raiseExit(p) @@ -158,10 +155,9 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} = result = true proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) = - var a, b, c: TLoc = default(TLoc) - initLocExpr(p, q[1], a) - initLocExpr(p, q[2], b) - initLocExpr(p, q[3], c) + var a = initLocExpr(p, q[1]) + var b = initLocExpr(p, q[2]) + var c = initLocExpr(p, q[3]) # but first produce the required index checks: if optBoundsCheck in p.options: genBoundsCheck(p, a, b, c) @@ -226,8 +222,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) = let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0]) result.add x & ", " & y else: - var a: TLoc = default(TLoc) - initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a) + var a: TLoc = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n) case skipTypes(a.t, abstractVar+{tyStatic}).kind of tyOpenArray, tyVarargs: if reifiedOpenArray(n): @@ -283,12 +278,11 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc = genAssignment(p, result, a, {}) proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} = - var a: TLoc = default(TLoc) - initLocExpr(p, n[0], a) + var a: TLoc = initLocExpr(p, n[0]) appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc]) proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) = - var a: TLoc = default(TLoc) + var a: TLoc if n.kind == nkStringToCString: genArgStringToCString(p, n, result, needsTmp) elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: @@ -296,14 +290,14 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need openArrayLoc(p, param.typ, n, result) elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and (optByRef notin param.options or not p.module.compileToCpp): - initLocExpr(p, n, a) + a = initLocExpr(p, n) if n.kind in {nkCharLit..nkNilLit}: addAddrLoc(p.config, literalsNeedsTmp(p, a), result) else: addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: - initLocExprSingleUse(p, n[0], a) + a = initLocExprSingleUse(p, n[0]) # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still # means '*T'. See posix.nim for lots of examples that do that in the wild. let callee = call[0] @@ -314,16 +308,16 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need else: addRdLoc(a, result) else: - initLocExprSingleUse(p, n, a) + a = initLocExprSingleUse(p, n) addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) #assert result != nil proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) = - var a: TLoc = default(TLoc) + var a: TLoc if n.kind == nkStringToCString: genArgStringToCString(p, n, result, needsTmp) else: - initLocExprSingleUse(p, n, a) + a = initLocExprSingleUse(p, n) addRdLoc(withTmpIfNeeded(p, a, needsTmp), result) import aliasanalysis @@ -423,9 +417,8 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) = res = res & "_actual".rope proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = - var op: TLoc = default(TLoc) # this is a hotspot in the compiler - initLocExpr(p, ri[0], op) + var op: TLoc = initLocExpr(p, ri[0]) # getUniqueType() is too expensive here: var typ = skipTypes(ri[0].typ, abstractInstOwned) assert(typ.kind == tyProc) @@ -447,8 +440,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)" const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists - var op: TLoc = default(TLoc) - initLocExpr(p, ri[0], op) + var op: TLoc = initLocExpr(p, ri[0]) # getUniqueType() is too expensive here: var typ = skipTypes(ri[0].typ, abstractInstOwned) @@ -490,8 +482,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = elif isHarmlessStore(p, canRaise, d): if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: - var list: TLoc = default(TLoc) - initLoc(list, locCall, d.lode, OnUnknown) + var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] else: @@ -502,8 +493,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = var tmp: TLoc getTemp(p, typ[0], tmp) assert(d.t != nil) # generate an assignment to d: - var list: TLoc = default(TLoc) - initLoc(list, locCall, d.lode, OnUnknown) + var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] else: @@ -685,8 +675,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro result.add(substr(pat, start, i - 1)) proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = - var op: TLoc = default(TLoc) - initLocExpr(p, ri[0], op) + var op: TLoc = initLocExpr(p, ri[0]) # getUniqueType() is too expensive here: var typ = skipTypes(ri[0].typ, abstractInst) assert(typ.kind == tyProc) @@ -710,8 +699,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = else: if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: - var list: TLoc - initLoc(list, locCall, d.lode, OnUnknown) + var list: TLoc = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: @@ -731,8 +719,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = # generates a crappy ObjC call - var op: TLoc = default(TLoc) - initLocExpr(p, ri[0], op) + var op: TLoc = initLocExpr(p, ri[0]) var pl = "[" # getUniqueType() is too expensive here: var typ = skipTypes(ri[0].typ, abstractInst) @@ -790,8 +777,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = pl.add("]") if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: - var list: TLoc = default(TLoc) - initLoc(list, locCall, ri, OnUnknown) + var list: TLoc = initLoc(locCall, ri, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 46a353dce004..e46a0470df9a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -479,10 +479,9 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = d = s # ``d`` is free, so fill it with ``s`` proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = - var a: TLoc = default(TLoc) if d.k != locNone: + var a: TLoc = initLoc(locData, n, OnStatic) # need to generate an assignment here - initLoc(a, locData, n, OnStatic) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -494,10 +493,9 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = d.r = r proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = - var a: TLoc = default(TLoc) if d.k != locNone: # need to generate an assignment here - initLoc(a, locExpr, n, s) + var a: TLoc = initLoc(locExpr, n, s) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -509,49 +507,42 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = d.r = r proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) = - var a, b: TLoc = default(TLoc) if d.k != locNone: internalError(p.config, e.info, "binaryStmt") - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) lineCg(p, cpsStmts, "$1 $2 $3;$n", [rdLoc(a), op, rdLoc(b)]) proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) = - var a, b: TLoc = default(TLoc) if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) lineCg(p, cpsStmts, "#$1($2, $3);$n", [cpname, byRefLoc(p, a), rdLoc(b)]) template unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a: TLoc = default(TLoc) if d.k != locNone: internalError(p.config, e.info, "unaryStmt") - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) lineCg(p, cpsStmts, frmt, [rdLoc(a)]) template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)])) template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc])) template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)])) template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)])) template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; @@ -591,11 +582,10 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = "nimAddInt64", "nimSubInt64" ] opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"] - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) # skipping 'range' is correct here as we'll generate a proper range check # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) @@ -625,11 +615,9 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = putIntoDest(p, d, e, res) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = - var - a: TLoc = default(TLoc) - t: PType + var t: PType assert(e[1].typ != nil) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) t = skipTypes(e.typ, abstractRange) if optOverflowCheck in p.options: var first = newRopeAppender() @@ -651,12 +639,11 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var - a, b: TLoc = default(TLoc) s, k: BiggestInt = 0 assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8 k = getSize(p.config, a.t) * 8 @@ -710,11 +697,10 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = assert(false, $op) proc genEqProc(p: BProc, e: PNode, d: var TLoc) = - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) if a.t.skipTypes(abstractInstOwned).callConv == ccClosure: putIntoDest(p, d, e, "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)]) @@ -730,10 +716,9 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) = proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var - a: TLoc = default(TLoc) t: PType assert(e[1].typ != nil) - initLocExpr(p, e[1], a) + var a = initLocExpr(p, e[1]) t = skipTypes(e.typ, abstractRange) template applyFormat(frmt: untyped) = @@ -767,16 +752,16 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = if e[0].typ.skipTypes(abstractInstOwned).kind == tyRef: d.storage = OnHeap else: - var a: TLoc = default(TLoc) + var a: TLoc var typ = e[0].typ if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: typ = typ.lastSon typ = typ.skipTypes(abstractInstOwned) if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: - initLocExprSingleUse(p, e[0][0], d) + d = initLocExprSingleUse(p, e[0][0]) return else: - initLocExprSingleUse(p, e[0], a) + a = initLocExprSingleUse(p, e[0]) if d.k == locNone: # dest = *a; <-- We do not know that 'dest' is on the heap! # It is completely wrong to set 'd.storage' here, unless it's not yet @@ -813,8 +798,7 @@ proc cowBracket(p: BProc; n: PNode) = if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions: let strCandidate = n[0] if strCandidate.typ.skipTypes(abstractInst).kind == tyString: - var a: TLoc = default(TLoc) - initLocExpr(p, strCandidate, a) + var a: TLoc = initLocExpr(p, strCandidate) linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) proc cow(p: BProc; n: PNode) {.inline.} = @@ -823,31 +807,28 @@ proc cow(p: BProc; n: PNode) {.inline.} = proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: - var a: TLoc = default(TLoc) - initLocExpr(p, e[0], a) + var a: TLoc = initLocExpr(p, e[0]) putIntoDest(p, d, e, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ): expr(p, e[0], d) else: - var a: TLoc = default(TLoc) - initLocExpr(p, e[0], a) + var a: TLoc = initLocExpr(p, e[0]) putIntoDest(p, d, e, addrLoc(p.config, a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = a.storage -proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = - initLocExpr(p, e[0], a) +proc genRecordFieldAux(p: BProc, e: PNode, d: var TLoc, a: var TLoc) = + a = initLocExpr(p, e[0]) if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = var - a: TLoc = default(TLoc) i: int = 0 - initLocExpr(p, e[0], a) + var a: TLoc = initLocExpr(p, e[0]) let tupType = a.t.skipTypes(abstractInst+{tyVar}) assert tupType.kind == tyTuple d.inheritLocation(a) @@ -900,7 +881,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = - var test, u, v: TLoc = default(TLoc) + var test, u, v: TLoc for i in 1.. 0: args.add(", ") case detectStrVersion(p.module) @@ -1238,8 +1213,7 @@ proc genEcho(p: BProc, n: PNode) = if n.len == 0: linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len]) else: - var a: TLoc = default(TLoc) - initLocExpr(p, n, a) + var a: TLoc = initLocExpr(p, n) linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len]) when false: p.module.includeHeader("") @@ -1273,7 +1247,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # appendChar(tmp0, 'z'); # asgn(s, tmp0); # } - var a = default(TLoc) + var a: TLoc var tmp: TLoc getTemp(p, e.typ, tmp) var L = 0 @@ -1281,7 +1255,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = var lens: Rope = "" for i in 0.. # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; - var a, b, dest, tmpL, call: TLoc = default(TLoc) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var tmpL: TLoc = default(TLoc) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) let seqType = skipTypes(e[1].typ, {tyVar}) - initLoc(call, locCall, e, OnHeap) + var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)" call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), @@ -1367,7 +1341,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = genRefAssign(p, a, call) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) - initLoc(dest, locExpr, e[2], OnHeap) + var dest = initLoc(locExpr, e[2], OnHeap) getIntTemp(p, tmpL) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)]) dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)]) @@ -1375,8 +1349,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = - var a: TLoc = default(TLoc) - initLocExpr(p, n[1], a) + var a: TLoc = initLocExpr(p, n[1]) specializeReset(p, a) when false: linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", @@ -1390,8 +1363,7 @@ proc genDefault(p: BProc; n: PNode; d: var TLoc) = proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = var sizeExpr = sizeExpr let typ = a.t - var b: TLoc = default(TLoc) - initLoc(b, locExpr, a.lode, OnHeap) + var b: TLoc = initLoc(locExpr, a.lode, OnHeap) let refType = typ.skipTypes(abstractInstOwned) assert refType.kind == tyRef let bt = refType.lastSon @@ -1417,8 +1389,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = localError(p.module.config, a.lode.info, "the destructor that is turned into a finalizer needs " & "to have the 'nimcall' calling convention") - var f: TLoc = default(TLoc) - initLocExpr(p, newSymNode(op), f) + var f: TLoc = initLocExpr(p, newSymNode(op)) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) if a.storage == OnHeap and usesWriteBarrier(p.config): @@ -1443,12 +1414,10 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = genObjectInit(p, cpsStmts, bt, a, constructRefObj) proc genNew(p: BProc, e: PNode) = - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) # 'genNew' also handles 'unsafeNew': if e.len == 3: - var se: TLoc = default(TLoc) - initLocExpr(p, e[2], se) + var se: TLoc = initLocExpr(p, e[2]) rawGenNew(p, a, se.rdLoc, needsInit = true) else: rawGenNew(p, a, "", needsInit = true) @@ -1456,8 +1425,7 @@ proc genNew(p: BProc, e: PNode) = proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = let seqtype = skipTypes(dest.t, abstractVarRange) - var call: TLoc = default(TLoc) - initLoc(call, locExpr, dest.lode, OnHeap) + var call: TLoc = initLoc(locExpr, dest.lode, OnHeap) if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(p.module.g.graph, dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc]) @@ -1482,9 +1450,8 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = genAssignment(p, dest, call, {}) proc genNewSeq(p: BProc, e: PNode) = - var a, b: TLoc = default(TLoc) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) if optSeqDestructors in p.config.globalOptions: let seqtype = skipTypes(e[1].typ, abstractVarRange) linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", @@ -1498,8 +1465,7 @@ proc genNewSeq(p: BProc, e: PNode) = proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) if optSeqDestructors in p.config.globalOptions: if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", @@ -1602,7 +1568,7 @@ proc lhsDoesAlias(a, b: PNode): bool = if isPartOf(a, y) != arNo: return true proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = - var arr = default(TLoc) + var arr: TLoc var tmp: TLoc = default(TLoc) # bug #668 let doesAlias = lhsDoesAlias(d.lode, n) @@ -1623,7 +1589,7 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = # generate call to newSeq before adding the elements per hand: genNewSeqAux(p, dest[], lit, n.len == 0) for i in 0..finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ @@ -1718,8 +1684,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Ro appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache]) proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = - var a: TLoc = default(TLoc) - initLocExpr(p, x, a) + var a: TLoc = initLocExpr(p, x) var dest = skipTypes(typ, typedescPtrs) var r = rdLoc(a) var nilCheck: Rope = "" @@ -1760,8 +1725,7 @@ proc genOf(p: BProc, n: PNode, d: var TLoc) = proc genRepr(p: BProc, e: PNode, d: var TLoc) = if optTinyRtti in p.config.globalOptions: localError(p.config, e.info, "'repr' is not available for --newruntime") - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var t = skipTypes(e[1].typ, abstractVarRange) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: @@ -1841,8 +1805,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = # ordinary static type information putIntoDest(p, d, e, genTypeInfoV2(p.module, t, e.info)) else: - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var nilCheck = "" # use the dynamic type stored at offset 0: var rt = newRopeAppender() @@ -1850,8 +1813,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, rt) proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var nilCheck = "" # use the dynamic type stored at offset 0: var rt = newRopeAppender() @@ -1859,8 +1821,7 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = putIntoDest(p, d, e, rt) template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = - var a: TLoc = default(TLoc) - initLocExpr(p, n[1], a) + var a: TLoc = initLocExpr(p, n[1]) a.r = ropecg(p.module, frmt, [rdLoc(a)]) a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR) if d.k == locNone: getTemp(p, n.typ, d) @@ -1876,11 +1837,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # Bug #9279, len(toOpenArray()) has to work: if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice: # magic: pass slice to openArray: - var m: TLoc = default(TLoc) - var b, c: TLoc = default(TLoc) - initLocExpr(p, a[1], m) - initLocExpr(p, a[2], b) - initLocExpr(p, a[3], c) + var m = initLocExpr(p, a[1]) + var b = initLocExpr(p, a[2]) + var c = initLocExpr(p, a[3]) if optBoundsCheck in p.options: genBoundsCheck(p, m, b, c) if op == mHigh: @@ -1907,15 +1866,14 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") of tyString: - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] putIntoDest(p, d, e, x) of tySequence: # we go through a temporary here because people write bullshit code. - var a, tmp: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var tmp: TLoc = default(TLoc) + var a = initLocExpr(p, e[1]) getIntTemp(p, tmp) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] @@ -1939,15 +1897,14 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = e[1] = makeAddr(e[1], p.module.idgen) genCall(p, e, d) return - var a, b, call: TLoc = default(TLoc) assert(d.k == locNone) var x = e[1] if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] - initLocExpr(p, x, a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, e[2]) let t = skipTypes(e[1].typ, {tyVar}) - initLoc(call, locCall, e, OnHeap) + var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)" call.r = ropecg(p.module, setLenPattern, [ @@ -1967,12 +1924,11 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: binaryStmtAddr(p, e, d, "setLengthStrV2") else: - var a, b, call: TLoc = default(TLoc) if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) - initLoc(call, locCall, e, OnHeap) + var call = initLoc(locCall, e, OnHeap) call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ rdLoc(a), rdLoc(b)]) genAssignment(p, a, call, {}) @@ -1985,11 +1941,10 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = # b = temp cowBracket(p, e[1]) cowBracket(p, e[2]) - var a, b = default(TLoc) var tmp: TLoc getTemp(p, skipTypes(e[1].typ, abstractVar), tmp) - initLocExpr(p, e[1], a) # eval a - initLocExpr(p, e[2], b) # eval b + var a = initLocExpr(p, e[1]) # eval a + var b = initLocExpr(p, e[2]) # eval b genAssignment(p, tmp, a, {}) genAssignment(p, a, b, {}) genAssignment(p, b, tmp, {}) @@ -2031,16 +1986,15 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)") template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc = default(TLoc) assert(d.k == locNone) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) var elem = newRopeAppender() rdSetElemLoc(p.config, b, a.t, elem) lineF(p, cpsStmts, frmt, [rdLoc(a), elem]) proc genInOp(p: BProc, e: PNode, d: var TLoc) = - var a, b, x, y: TLoc = default(TLoc) + var a, b, x, y: TLoc if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]): # a set constructor but not a constant set: # do not emit the set, but generate a bunch of comparisons; and if we do @@ -2050,19 +2004,19 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = e[2][0] else: e[2] - initLocExpr(p, ea, a) - initLoc(b, locExpr, e, OnUnknown) + a = initLocExpr(p, ea) + b = initLoc(locExpr, e, OnUnknown) if e[1].len > 0: b.r = rope("(") for i in 0..= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: - initLocExpr(p, it, x) + x = initLocExpr(p, it) b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) if i < e[1].len - 1: b.r.add(" || ") b.r.add(")") @@ -2073,8 +2027,8 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = else: assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + a = initLocExpr(p, e[1]) + b = initLocExpr(p, e[2]) genInExprAux(p, e, a, b, d) proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = @@ -2090,7 +2044,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "&", "|", "& ~"] - var a, b = default(TLoc) + var a, b: TLoc var i: TLoc var setType = skipTypes(e[1].typ, abstractVar) var size = int(getSize(p.config, setType)) @@ -2128,13 +2082,12 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n") of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n") of mCard: - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) of mLtSet, mLeSet: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + a = initLocExpr(p, e[1]) + b = initLocExpr(p, e[2]) if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d) if op == mLtSet: linefmt(p, cpsStmts, lookupOpr[mLtSet], @@ -2143,17 +2096,16 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = linefmt(p, cpsStmts, lookupOpr[mLeSet], [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size])) of mMulSet, mPlusSet, mMinusSet: # we inline the simple for loop for better code generation: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + a = initLocExpr(p, e[1]) + b = initLocExpr(p, e[2]) if d.k == locNone: getTemp(p, setType, d) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) $n" & @@ -2171,8 +2123,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray} # we use whatever C gives us. Except if we have a value-type, we need to go # through its address: - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) let etyp = skipTypes(e.typ, abstractRange+{tyOwned}) let srcTyp = skipTypes(e[1].typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: @@ -2232,9 +2183,8 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = genSomeCast(p, e, d) proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc = default(TLoc) + var a: TLoc = initLocExpr(p, n[0]) var dest = skipTypes(n.typ, abstractVar) - initLocExpr(p, n[0], a) if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures): discard "no need to generate a check because it was disabled" @@ -2287,31 +2237,29 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = genSomeCast(p, e, d) proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc = default(TLoc) - initLocExpr(p, n[0], a) + var a: TLoc = initLocExpr(p, n[0]) putIntoDest(p, d, n, ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]), # "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc], a.storage) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc = default(TLoc) - initLocExpr(p, n[0], a) + var a: TLoc = initLocExpr(p, n[0]) putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.storage) gcUsage(p.config, n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = - var x: TLoc = default(TLoc) + var x: TLoc var a = e[1] var b = e[2] if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": - initLocExpr(p, e[2], x) + x = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": - initLocExpr(p, e[1], x) + x = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) else: @@ -2320,11 +2268,10 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if {optNaNCheck, optInfCheck} * p.options != {}: const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"] - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))", [opr[m], rdLoc(a), rdLoc(b), getSimpleTypeDesc(p.module, e[1].typ)])) @@ -2345,23 +2292,21 @@ proc skipAddr(n: PNode): PNode = result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n proc genWasMoved(p: BProc; n: PNode) = - var a: TLoc = default(TLoc) + var a: TLoc let n1 = n[1].skipAddr if p.withinBlockLeaveActions > 0 and notYetAlive(n1): discard else: - initLocExpr(p, n1, a, {lfEnforceDeref}) + a = initLocExpr(p, n1, {lfEnforceDeref}) resetLoc(p, a) #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) proc genMove(p: BProc; n: PNode; d: var TLoc) = - var a: TLoc = default(TLoc) - initLocExpr(p, n[1].skipAddr, a) + var a: TLoc = initLocExpr(p, n[1].skipAddr) if n.len == 4: # generated by liftdestructors: - var src: TLoc = default(TLoc) - initLocExpr(p, n[2], src) + var src: TLoc = initLocExpr(p, n[2]) linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)]) genStmts(p, n[3]) linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)]) @@ -2387,8 +2332,7 @@ proc genDestroy(p: BProc; n: PNode) = let t = arg.typ.skipTypes(abstractInst) case t.kind of tyString: - var a: TLoc = default(TLoc) - initLocExpr(p, arg, a) + var a: TLoc = initLocExpr(p, arg) if optThreads in p.config.globalOptions: linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #deallocShared($1.p);$n" & @@ -2398,8 +2342,7 @@ proc genDestroy(p: BProc; n: PNode) = " #dealloc($1.p);$n" & "}$n", [rdLoc(a)]) of tySequence: - var a: TLoc = default(TLoc) - initLocExpr(p, arg, a) + var a: TLoc = initLocExpr(p, arg) linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & "}$n", @@ -2416,8 +2359,7 @@ proc genDispose(p: BProc; n: PNode) = when false: let elemType = n[1].typ.skipTypes(abstractVar).lastSon - var a: TLoc = default(TLoc) - initLocExpr(p, n[1].skipAddr, a) + var a: TLoc = initLocExpr(p, n[1].skipAddr) if isFinal(elemType): if elemType.destructor != nil: @@ -2469,11 +2411,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: - var a, b: TLoc = default(TLoc) assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyDistinct}) let res = binaryArithOverflowRaw(p, ranged, a, b, @@ -2487,10 +2428,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optSeqDestructors in p.config.globalOptions: binaryStmtAddr(p, e, d, "nimAddCharV1") else: - var dest, b, call: TLoc = default(TLoc) - initLoc(call, locCall, e, OnHeap) - initLocExpr(p, e[1], dest) - initLocExpr(p, e[2], b) + var call = initLoc(locCall, e, OnHeap) + var dest = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) @@ -2525,8 +2465,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNew: genNew(p, e) of mNewFinalize: if optTinyRtti in p.config.globalOptions: - var a: TLoc = default(TLoc) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) rawGenNew(p, a, "", needsInit = true) gcUsage(p.config, e) else: @@ -2620,10 +2559,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = localError(p.config, e.info, "for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on") - var a, b: TLoc = default(TLoc) let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] - initLocExpr(p, x, a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, e[2]) genDeepCopy(p, a, b) of mDotDot, mEqCString: genCall(p, e, d) of mWasMoved: genWasMoved(p, e) @@ -2646,7 +2584,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var - a, b = default(TLoc) + a, b: TLoc var idx: TLoc if nfAllConst in e.flags: var elem = newRopeAppender() @@ -2661,8 +2599,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = for it in e.sons: if it.kind == nkRange: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter - initLocExpr(p, it[0], a) - initLocExpr(p, it[1], b) + a = initLocExpr(p, it[0]) + b = initLocExpr(p, it[1]) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) var bb = newRopeAppender() @@ -2671,7 +2609,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), aa, bb]) else: - initLocExpr(p, it, a) + a = initLocExpr(p, it) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", @@ -2683,8 +2621,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = for it in e.sons: if it.kind == nkRange: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter - initLocExpr(p, it[0], a) - initLocExpr(p, it[1], b) + a = initLocExpr(p, it[0]) + b = initLocExpr(p, it[1]) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) var bb = newRopeAppender() @@ -2694,7 +2632,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [ rdLoc(idx), rdLoc(d), aa, bb, rope(ts)]) else: - initLocExpr(p, it, a) + a = initLocExpr(p, it) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) lineF(p, cpsStmts, @@ -2702,7 +2640,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = [rdLoc(d), aa, rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = - var rec: TLoc = default(TLoc) + var rec: TLoc if not handleConstExpr(p, n, d): let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized @@ -2719,7 +2657,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = for i in 0..Sup" else: ".Sup") for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup") putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage) @@ -3156,8 +3090,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = let ex = n[0] if ex.kind != nkEmpty: genLineDir(p, n) - var a: TLoc = default(TLoc) - initLocExprSingleUse(p, ex, a) + var a: TLoc = initLocExprSingleUse(p, ex) line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt, nkHiddenTryStmt: @@ -3478,12 +3411,10 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul if n[0].kind == nkNilLit: result.add "{NIM_NIL,NIM_NIL}" else: - var d: TLoc = default(TLoc) - initLocExpr(p, n[0], d) + var d: TLoc = initLocExpr(p, n[0]) result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] else: - var d: TLoc = default(TLoc) - initLocExpr(p, n, d) + var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) of tyArray, tyVarargs: genConstSimpleList(p, n, isConst, result) @@ -3511,10 +3442,8 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: genStringLiteralV2Const(p.module, n, isConst, result) else: - var d: TLoc = default(TLoc) - initLocExpr(p, n, d) + var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) else: - var d: TLoc = default(TLoc) - initLocExpr(p, n, d) + var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 1751321f3ffa..b79eaf346e67 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -72,7 +72,6 @@ template startBlock(p: BProc, start: FormatStr = "{$n", proc endBlock(p: BProc) proc genVarTuple(p: BProc, n: PNode) = - var tup, field: TLoc = default(TLoc) if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") # if we have a something that's been captured, use the lowering instead: @@ -96,7 +95,7 @@ proc genVarTuple(p: BProc, n: PNode) = startBlock(p) genLineDir(p, n) - initLocExpr(p, n[^1], tup) + var tup = initLocExpr(p, n[^1]) var t = tup.t.skipTypes(abstractInst) for i in 0.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", @@ -1495,8 +1484,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = of nkSym: var sym = it.sym if sym.kind in {skProc, skFunc, skIterator, skMethod}: - var a: TLoc = default(TLoc) - initLocExpr(p, it, a) + var a: TLoc = initLocExpr(p, it) res.add($rdLoc(a)) elif sym.kind == skType: res.add($getTypeDesc(p.module, sym.typ)) @@ -1508,8 +1496,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = res.add($getTypeDesc(p.module, it.typ)) else: discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs)) - var a: TLoc = default(TLoc) - initLocExpr(p, it, a) + var a: TLoc = initLocExpr(p, it) res.add($a.rdLoc) if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props: @@ -1611,11 +1598,10 @@ when false: expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = - var a = default(TLoc) var tmp: TLoc var dotExpr = e[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] - initLocExpr(p, e[0], a) + var a = initLocExpr(p, e[0]) getTemp(p, a.t, tmp) expr(p, e[1], tmp) if p.inUncheckedAssignSection == 0: @@ -1634,9 +1620,8 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = else: let le = e[0] let ri = e[1] - var a: TLoc = default(TLoc) + var a: TLoc = initLoc(locNone, le, OnUnknown) discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar) - initLoc(a, locNone, le, OnUnknown) a.flags.incl(lfEnforceDeref) a.flags.incl(lfPrepareForMutation) genLineDir(p, le) # it can be a nkBracketExpr, which may raise diff --git a/compiler/cgen.nim b/compiler/cgen.nim index af30f546ecd0..f2483e2de367 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -61,12 +61,10 @@ proc findPendingModule(m: BModule, s: PSym): BModule = var ms = getModule(s) result = m.g.modules[ms.position] -proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}) = - result.k = k - result.storage = s - result.lode = lode - result.r = "" - result.flags = flags +proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc = + result = TLoc(k: k, storage: s, lode: lode, + r: "", flags: flags + ) proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} = # fills the loc if it is not already initialized @@ -483,8 +481,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): if containsGcRef: - var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.lode, OnStack) + var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: @@ -514,8 +511,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): if containsGarbageCollectedRef(loc.t): - var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.lode, OnStack) + var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: @@ -731,12 +727,12 @@ proc genLiteral(p: BProc, n: PNode; result: var Rope) proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int) proc raiseExit(p: BProc) -proc initLocExpr(p: BProc, e: PNode, result: var TLoc, flags: TLocFlags = {}) = - initLoc(result, locNone, e, OnUnknown, flags) +proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc = + result = initLoc(locNone, e, OnUnknown, flags) expr(p, e, result) -proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = - initLoc(result, locNone, e, OnUnknown) +proc initLocExprSingleUse(p: BProc, e: PNode): TLoc = + result = initLoc(locNone, e, OnUnknown) if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone): # We cannot check for tfNoSideEffect here because of mutable parameters. discard "bug #8202; enforce evaluation order for nested calls for C++ too" @@ -827,8 +823,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = var p = newProc(nil, m) p.options.excl optStackTrace p.flags.incl nimErrorFlagDisabled - var dest: TLoc - initLoc(dest, locTemp, lib.path, OnStack) + var dest: TLoc = initLoc(locTemp, lib.path, OnStack) dest.r = getTempName(m) appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)]) @@ -863,11 +858,10 @@ proc symInDynamicLib(m: BModule, sym: PSym) = inc(m.labels, 2) if isCall: let n = lib.path - var a: TLoc = default(TLoc) - initLocExpr(m.initProc, n[0], a) + var a: TLoc = initLocExpr(m.initProc, n[0]) var params = rdLoc(a) & "(" for i in 1.. Date: Thu, 10 Aug 2023 16:26:23 +0800 Subject: [PATCH 153/347] fix #19304 Borrowing std/times.format causes Error: illformed AST (#20659) * fix #19304 Borrowing std/times.format causes Error: illformed AST * follow suggestions * mitigate for #4121 * improve error message --- compiler/semcall.nim | 44 +++++++++++++++++++++---------- compiler/semdata.nim | 2 ++ compiler/semstmts.nim | 33 ++++++++++++++--------- tests/distinct/tinvalidborrow.nim | 23 +++++++++++++--- tests/stdlib/t19304.nim | 7 +++++ 5 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 tests/stdlib/t19304.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index e5f2e820b658..073c00c37e22 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -771,18 +771,25 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = else: result = explicitGenericInstError(c, n) -proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = +proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] = # Searches for the fn in the symbol table. If the parameter lists are suitable # for borrowing the sym in the symbol table is returned, else nil. # New approach: generate fn(x, y, z) where x, y, z have the proper types # and use the overloading resolution mechanism: - result = nil + const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct} + + template getType(isDistinct: bool; t: PType):untyped = + if isDistinct: t.baseOfDistinct(c.graph, c.idgen) else: t + + result = default(tuple[s: PSym, state: TBorrowState]) var call = newNodeI(nkCall, fn.info) var hasDistinct = false + var isDistinct: bool + var x: PType + var t: PType call.add(newIdentNode(fn.name, fn.info)) for i in 1.. 0: - s.typ.n[0] = b.typ.n[0] - s.typ.flags = b.typ.flags - else: - localError(c.config, n.info, errNoSymbolToBorrowFromFound) + var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s) + case state + of bsMatch: + # store the alias: + n[bodyPos] = newSymNode(b) + # Carry over the original symbol magic, this is necessary in order to ensure + # the semantic pass is correct + s.magic = b.magic + if b.typ != nil and b.typ.len > 0: + s.typ.n[0] = b.typ.n[0] + s.typ.flags = b.typ.flags + of bsNoDistinct: + localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless") + of bsReturnNotMatch: + localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ[0])) + of bsGeneric: + localError(c.config, n.info, "borrow with generic parameter is not supported") + of bsNotSupported: + localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s) + else: + localError(c.config, n.info, errNoSymbolToBorrowFromFound) proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = ## Swap nodes that are (skResult) symbols to d(estination)Node. diff --git a/tests/distinct/tinvalidborrow.nim b/tests/distinct/tinvalidborrow.nim index 08148608d6c8..d4b19fa8decd 100644 --- a/tests/distinct/tinvalidborrow.nim +++ b/tests/distinct/tinvalidborrow.nim @@ -2,12 +2,19 @@ discard """ cmd: "nim check --hints:off --warnings:off $file" action: "reject" nimout:''' -tinvalidborrow.nim(18, 3) Error: only a 'distinct' type can borrow `.` -tinvalidborrow.nim(19, 3) Error: only a 'distinct' type can borrow `.` -tinvalidborrow.nim(20, 1) Error: no symbol to borrow from found +tinvalidborrow.nim(25, 3) Error: only a 'distinct' type can borrow `.` +tinvalidborrow.nim(26, 3) Error: only a 'distinct' type can borrow `.` +tinvalidborrow.nim(27, 1) Error: borrow proc without distinct type parameter is meaningless +tinvalidborrow.nim(36, 1) Error: borrow with generic parameter is not supported +tinvalidborrow.nim(41, 1) Error: borrow from proc return type mismatch: 'T' +tinvalidborrow.nim(42, 1) Error: borrow from '[]=' is not supported ''' """ + + + + # bug #516 type @@ -23,3 +30,13 @@ var d, e: TAtom discard( $(d == e) ) + +# issue #4121 +type HeapQueue[T] = distinct seq[T] +proc len*[T](h: HeapQueue[T]): int {.borrow.} + +# issue #3564 +type vec4[T] = distinct array[4, float32] + +proc `[]`(v: vec4, i: int): float32 {.borrow.} +proc `[]=`(v: vec4, i: int, va: float32) {.borrow.} diff --git a/tests/stdlib/t19304.nim b/tests/stdlib/t19304.nim new file mode 100644 index 000000000000..5e8795ac563f --- /dev/null +++ b/tests/stdlib/t19304.nim @@ -0,0 +1,7 @@ +import times + +type DjangoDateTime* = distinct DateTime + +# proc toTime*(x: DjangoDateTime): Time {.borrow.} # <-- works +proc format*(x: DjangoDateTime, f: TimeFormat, + loc: DateTimeLocale = DefaultLocale): string {.borrow.} From 05f7c4f79db096581352cbe20666f82300d21580 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 10 Aug 2023 16:41:24 +0800 Subject: [PATCH 154/347] fixes a typo (#22437) --- changelogs/changelog_2_2_0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md index 341f41045a7f..0a293d35fbd1 100644 --- a/changelogs/changelog_2_2_0.md +++ b/changelogs/changelog_2_2_0.md @@ -1,4 +1,4 @@ -# v2.2.1 - 2023-mm-dd +# v2.2.0 - 2023-mm-dd ## Changes affecting backward compatibility From 8625e712503bb36e29ed24a28d484fe7d5af05fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Thu, 10 Aug 2023 13:15:23 +0100 Subject: [PATCH 155/347] adds support for functor in member (#22433) * adds support for functor in member * improves functor test --- compiler/ccgtypes.nim | 7 ++++++- tests/cpp/tmember.nim | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 6bac84e95634..0b8cca77e0ac 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1149,14 +1149,19 @@ proc isReloadable(m: BModule; prc: PSym): bool = proc isNonReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags -proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual: var bool; isCtor: bool) = +proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual: var bool; isCtor: bool, isFunctor=false) = var afterParams: string = "" if scanf(val, "$*($*)$s$*", name, params, afterParams): + if name.strip() == "operator" and params == "": #isFunctor? + parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isCtor, true) + return isFnConst = afterParams.find("const") > -1 isOverride = afterParams.find("override") > -1 isMemberVirtual = name.find("virtual ") > -1 if isMemberVirtual: name = name.replace("virtual ", "") + if isFunctor: + name = "operator ()" if isCtor: discard scanf(afterParams, ":$s$*", superCall) else: diff --git a/tests/cpp/tmember.nim b/tests/cpp/tmember.nim index 3f498c722488..07bd5e0ee315 100644 --- a/tests/cpp/tmember.nim +++ b/tests/cpp/tmember.nim @@ -7,6 +7,7 @@ false hello foo hello boo hello boo +FunctorSupport! destructing destructing ''' @@ -51,3 +52,13 @@ let booAsFoo = cast[FooPtr](newCpp[Boo]()) foo.salute() boo.salute() booAsFoo.salute() + +type + NimFunctor = object + discard +proc invoke(f: NimFunctor, n:int) {.member:"operator ()('2 #2)" .} = + echo "FunctorSupport!" + +{.experimental: "callOperator".} +proc `()`(f: NimFunctor, n:int) {.importcpp:"#(@)" .} +NimFunctor()(1) From 8523b543d6034d8545a4db256bff83b73c927033 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 10 Aug 2023 20:17:15 +0800 Subject: [PATCH 156/347] `getTemp` and friends now return `TLoc` as requested (#22440) getTemp and friends now return `TLoc` --- compiler/ccgcalls.nim | 37 +++++++++++------------- compiler/ccgexprs.nim | 66 +++++++++++++++++++------------------------ compiler/ccgreset.nim | 3 +- compiler/ccgstmts.nim | 17 ++++++----- compiler/ccgtrav.nim | 8 ++---- compiler/cgen.nim | 6 ++-- 6 files changed, 60 insertions(+), 77 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index b446e83607a7..80b534cee6c6 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -88,7 +88,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri): # Great, we can use 'd': - if d.k == locNone: getTemp(p, typ[0], d, needsInit=true) + if d.k == locNone: d = getTemp(p, typ[0], needsInit=true) elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: discard "resetLoc(p, d)" @@ -96,8 +96,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, pl.add(");\n") line(p, cpsStmts, pl) else: - var tmp: TLoc - getTemp(p, typ[0], tmp, needsInit=true) + var tmp: TLoc = getTemp(p, typ[0], needsInit=true) pl.add(addrLoc(p.config, tmp)) pl.add(");\n") line(p, cpsStmts, pl) @@ -115,24 +114,23 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, excl d.flags, lfSingleUse else: if d.k == locNone and p.splitDecls == 0: - getTempCpp(p, typ[0], d, pl) + d = getTempCpp(p, typ[0], pl) else: - if d.k == locNone: getTemp(p, typ[0], d) + if d.k == locNone: d = getTemp(p, typ[0]) var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) elif isHarmlessStore(p, canRaise, d): - if d.k == locNone: getTemp(p, typ[0], d) + if d.k == locNone: d = getTemp(p, typ[0]) assert(d.t != nil) # generate an assignment to d: var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) else: - var tmp: TLoc - getTemp(p, typ[0], tmp, needsInit=true) + var tmp: TLoc = getTemp(p, typ[0], needsInit=true) var list = initLoc(locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, tmp, list, {}) # no need for deep copying @@ -268,13 +266,13 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = # Also don't regress for non ARC-builds, too risky. if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and getSize(p.config, a.lode.typ) < 1024: - getTemp(p, a.lode.typ, result, needsInit=false) + result = getTemp(p, a.lode.typ, needsInit=false) genAssignment(p, result, a, {}) else: result = a proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc = - getTemp(p, a.lode.typ, result, needsInit=false) + result = getTemp(p, a.lode.typ, needsInit=false) genAssignment(p, result, a, {}) proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} = @@ -465,7 +463,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri): # Great, we can use 'd': if d.k == locNone: - getTemp(p, typ[0], d, needsInit=true) + d = getTemp(p, typ[0], needsInit=true) elif d.k notin {locTemp} and not hasNoInit(ri): # reset before pass as 'result' var: discard "resetLoc(p, d)" @@ -473,14 +471,13 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genCallPattern() if canRaise: raiseExit(p) else: - var tmp: TLoc - getTemp(p, typ[0], tmp, needsInit=true) + var tmp: TLoc = getTemp(p, typ[0], needsInit=true) pl.add(addrLoc(p.config, tmp)) genCallPattern() if canRaise: raiseExit(p) genAssignment(p, d, tmp, {}) # no need for deep copying elif isHarmlessStore(p, canRaise, d): - if d.k == locNone: getTemp(p, typ[0], d) + if d.k == locNone: d = getTemp(p, typ[0]) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: @@ -490,8 +487,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = genAssignment(p, d, list, {}) # no need for deep copying if canRaise: raiseExit(p) else: - var tmp: TLoc - getTemp(p, typ[0], tmp) + var tmp: TLoc = getTemp(p, typ[0]) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) if tfIterator in typ.flags: @@ -697,7 +693,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = d.r = pl excl d.flags, lfSingleUse else: - if d.k == locNone: getTemp(p, typ[0], d) + if d.k == locNone: d = getTemp(p, typ[0]) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, d.lode, OnUnknown) list.r = pl @@ -761,21 +757,20 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: # We already got a temp. Great, special case it: - if d.k == locNone: getTemp(p, typ[0], d, needsInit=true) + if d.k == locNone: d = getTemp(p, typ[0], needsInit=true) pl.add("Result: ") pl.add(addrLoc(p.config, d)) pl.add("];\n") line(p, cpsStmts, pl) else: - var tmp: TLoc - getTemp(p, typ[0], tmp, needsInit=true) + var tmp: TLoc = getTemp(p, typ[0], needsInit=true) pl.add(addrLoc(p.config, tmp)) pl.add("];\n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: pl.add("]") - if d.k == locNone: getTemp(p, typ[0], d) + if d.k == locNone: d = getTemp(p, typ[0]) assert(d.t != nil) # generate an assignment to d: var list: TLoc = initLoc(locCall, ri, OnUnknown) list.r = pl diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index e46a0470df9a..dd47d1d1f885 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -348,8 +348,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc]) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: - var tmp: TLoc - getTemp(p, ty, tmp) + var tmp: TLoc = getTemp(p, ty) linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", [dest.rdLoc, src.rdLoc, tmp.rdLoc]) linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) @@ -431,8 +430,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genDeepCopy(p: BProc; dest, src: TLoc) = template addrLocOrTemp(a: TLoc): Rope = if a.k == locExpr: - var tmp: TLoc - getTemp(p, a.t, tmp) + var tmp: TLoc = getTemp(p, a.t) genAssignment(p, tmp, a, {}) addrLoc(p.config, tmp) else: @@ -1169,8 +1167,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: var L: TLabel - tmp: TLoc - getTemp(p, e.typ, tmp) # force it into a temp! + var tmp: TLoc = getTemp(p, e.typ) # force it into a temp! inc p.splitDecls expr(p, e[1], tmp) L = getLabel(p) @@ -1248,8 +1245,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # asgn(s, tmp0); # } var a: TLoc - var tmp: TLoc - getTemp(p, e.typ, tmp) + var tmp: TLoc = getTemp(p, e.typ) var L = 0 var appends: Rope = "" var lens: Rope = "" @@ -1321,7 +1317,6 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq &= x --> # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; - var tmpL: TLoc = default(TLoc) var a = initLocExpr(p, e[1]) var b = initLocExpr(p, e[2]) let seqType = skipTypes(e[1].typ, {tyVar}) @@ -1342,7 +1337,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) var dest = initLoc(locExpr, e[2], OnHeap) - getIntTemp(p, tmpL) + var tmpL = getIntTemp(p) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)]) dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)]) genAssignment(p, dest, b, {needToCopy}) @@ -1357,7 +1352,7 @@ proc genReset(p: BProc, n: PNode) = genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)]) proc genDefault(p: BProc; n: PNode; d: var TLoc) = - if d.k == locNone: getTemp(p, n.typ, d, needsInit=true) + if d.k == locNone: d = getTemp(p, n.typ, needsInit=true) else: resetLoc(p, d) proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = @@ -1467,7 +1462,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) var a: TLoc = initLocExpr(p, e[1]) if optSeqDestructors in p.config.globalOptions: - if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) + if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype), @@ -1520,10 +1515,10 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or (isPartOf(d.lode, e) != arNo) - var tmp: TLoc = TLoc() + var tmp: TLoc = default(TLoc) var r: Rope if useTemp: - getTemp(p, t, tmp) + tmp = getTemp(p, t) r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags) @@ -1574,9 +1569,9 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = let doesAlias = lhsDoesAlias(d.lode, n) let dest = if doesAlias: addr(tmp) else: addr(d) if doesAlias: - getTemp(p, n.typ, tmp) + tmp = getTemp(p, n.typ) elif d.k == locNone: - getTemp(p, n.typ, d) + d = getTemp(p, n.typ) var lit = newRopeAppender() intLiteral(n.len, lit) @@ -1609,7 +1604,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = genSeqConstr(p, n[1], d) return if d.k == locNone: - getTemp(p, n.typ, d) + d = getTemp(p, n.typ) var a = initLocExpr(p, n[1]) # generate call to newSeq before adding the elements per hand: let L = toInt(lengthOrd(p.config, n[1].typ)) @@ -1634,8 +1629,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit]) genAssignment(p, elem, arr, {needToCopy}) else: - var i: TLoc - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) + var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, L]) elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)]) @@ -1824,7 +1818,7 @@ template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc = initLocExpr(p, n[1]) a.r = ropecg(p.module, frmt, [rdLoc(a)]) a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR) - if d.k == locNone: getTemp(p, n.typ, d) + if d.k == locNone: d = getTemp(p, n.typ) genAssignment(p, d, a, {}) gcUsage(p.config, n) @@ -1872,9 +1866,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = putIntoDest(p, d, e, x) of tySequence: # we go through a temporary here because people write bullshit code. - var tmp: TLoc = default(TLoc) + var tmp: TLoc = getIntTemp(p) var a = initLocExpr(p, e[1]) - getIntTemp(p, tmp) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x]) @@ -1941,8 +1934,7 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = # b = temp cowBracket(p, e[1]) cowBracket(p, e[2]) - var tmp: TLoc - getTemp(p, skipTypes(e[1].typ, abstractVar), tmp) + var tmp: TLoc = getTemp(p, skipTypes(e[1].typ, abstractVar)) var a = initLocExpr(p, e[1]) # eval a var b = initLocExpr(p, e[2]) # eval b genAssignment(p, tmp, a, {}) @@ -2085,10 +2077,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) of mLtSet, mLeSet: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter + i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter a = initLocExpr(p, e[1]) b = initLocExpr(p, e[2]) - if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d) + if d.k == locNone: d = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool)) if op == mLtSet: linefmt(p, cpsStmts, lookupOpr[mLtSet], [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) @@ -2103,10 +2095,10 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size])) of mMulSet, mPlusSet, mMinusSet: # we inline the simple for loop for better code generation: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter + i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter a = initLocExpr(p, e[1]) b = initLocExpr(p, e[2]) - if d.k == locNone: getTemp(p, setType, d) + if d.k == locNone: d = getTemp(p, setType) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) $n" & " $3[$1] = $4[$1] $6 $5[$1];$n", [ @@ -2311,7 +2303,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = genStmts(p, n[3]) linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)]) else: - if d.k == locNone: getTemp(p, n.typ, d) + if d.k == locNone: d = getTemp(p, n.typ) if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: genAssignment(p, d, a, {}) var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved) @@ -2376,7 +2368,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = prepareForMutation = e[1].kind == nkHiddenDeref and e[1].typ.skipTypes(abstractInst).kind == tyString and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}) - if d.k == locNone: getTemp(p, e.typ, d) + if d.k == locNone: d = getTemp(p, e.typ) linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y]) when false: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & @@ -2591,14 +2583,14 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = genSetNode(p, e, elem) putIntoDest(p, d, e, elem) else: - if d.k == locNone: getTemp(p, e.typ, d) + if d.k == locNone: d = getTemp(p, e.typ) if getSize(p.config, e.typ) > 8: # big set: linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n", [rdLoc(d), getTypeDesc(p.module, e.typ)]) for it in e.sons: if it.kind == nkRange: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter + idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter a = initLocExpr(p, it[0]) b = initLocExpr(p, it[1]) var aa = newRopeAppender() @@ -2620,7 +2612,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for it in e.sons: if it.kind == nkRange: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter + idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter a = initLocExpr(p, it[0]) b = initLocExpr(p, it[1]) var aa = newRopeAppender() @@ -2650,9 +2642,9 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = let doesAlias = lhsDoesAlias(d.lode, n) let dest = if doesAlias: addr(tmp) else: addr(d) if doesAlias: - getTemp(p, n.typ, tmp) + tmp = getTemp(p, n.typ) elif d.k == locNone: - getTemp(p, n.typ, d) + d = getTemp(p, n.typ) for i in 0..") if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) genLineDir(p, t) inc(p.labels, 2) @@ -1187,7 +1187,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = expr(p, body, d) if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) genLineDir(p, t) cgsym(p.module, "popCurrentExceptionEx") let fin = if t[^1].kind == nkFinally: t[^1] else: nil @@ -1265,7 +1265,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = p.flags.incl nimErrorFlagAccessed if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) expr(p, t[0], d) @@ -1372,7 +1372,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = # propagateCurrentException(); # if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) let quirkyExceptions = p.config.exc == excQuirky or (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags) if not quirkyExceptions: @@ -1598,11 +1598,10 @@ when false: expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = - var tmp: TLoc var dotExpr = e[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] var a = initLocExpr(p, e[0]) - getTemp(p, a.t, tmp) + var tmp: TLoc = getTemp(p, a.t) expr(p, e[1], tmp) if p.inUncheckedAssignSection == 0: let field = dotExpr[1].sym diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 9af33d45e886..adad9df3e628 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -21,7 +21,7 @@ const proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) proc genCaseRange(p: BProc, branch: PNode) -proc getTemp(p: BProc, t: PType, result: out TLoc; needsInit=false) +proc getTemp(p: BProc, t: PType, needsInit=false): TLoc proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; typ: PType) = @@ -74,8 +74,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = genTraverseProc(c, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(c.p.config, typ[0]) - var i: TLoc - getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) + var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) var oldCode = p.s(cpsStmts) freeze oldCode linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", @@ -119,8 +118,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var p = c.p assert typ.kind == tySequence - var i: TLoc - getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) + var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) var oldCode = p.s(cpsStmts) freeze oldCode var a: TLoc = TLoc(r: accessor) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f2483e2de367..de15e8ca4f75 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -538,7 +538,7 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = if not immediateAsgn: constructLoc(p, v.loc) -proc getTemp(p: BProc, t: PType, result: out TLoc; needsInit=false) = +proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = inc(p.labels) result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) @@ -556,13 +556,13 @@ proc getTemp(p: BProc, t: PType, result: out TLoc; needsInit=false) = echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo writeStackTrace() -proc getTempCpp(p: BProc, t: PType, result: out TLoc; value: Rope) = +proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc = inc(p.labels) result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value]) -proc getIntTemp(p: BProc, result: out TLoc) = +proc getIntTemp(p: BProc): TLoc = inc(p.labels) result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt), From 7be2e2bef545e68ac3d88876fe7073a033fbb5f4 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 10 Aug 2023 20:26:40 +0800 Subject: [PATCH 157/347] replaces `doAssert false` with `raiseAssert` for unreachable branches, which works better with strictdefs (#22436) replaces `doAssert false` with `raiseAssert`, which works better with strictdefs --- compiler/ast.nim | 2 +- compiler/ccgreset.nim | 2 +- compiler/ccgtypes.nim | 6 +++--- compiler/depends.nim | 3 +-- compiler/dfa.nim | 2 +- compiler/docgen.nim | 5 ++--- compiler/jsgen.nim | 3 +-- compiler/liftdestructors.nim | 2 +- compiler/main.nim | 6 +++--- compiler/nim.nim | 2 +- compiler/options.nim | 2 +- compiler/pipelines.nim | 8 +++----- compiler/ropes.nim | 8 ++++---- compiler/scriptconfig.nim | 2 +- compiler/sem.nim | 4 ++-- compiler/semfold.nim | 3 +-- compiler/tccgen.nim | 2 +- compiler/vmconv.nim | 4 ++-- compiler/vmgen.nim | 4 ++-- compiler/vmhooks.nim | 4 +--- compiler/vmops.nim | 2 +- 21 files changed, 34 insertions(+), 42 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index d62b563682eb..99647e2930c3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1499,7 +1499,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode = result = newNode(nkIntLit) of tyStatic: # that's a pre-existing bug, will fix in another PR result = newNode(nkIntLit) - else: doAssert false, $kind + else: raiseAssert $kind result.intVal = intVal result.typ = typ diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index 0976a3356156..bd0e2a58a346 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -94,7 +94,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = of ctInt8, ctInt16, ctInt32, ctInt64: lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) else: - doAssert false, "unexpected set type kind" + raiseAssert "unexpected set type kind" of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 0b8cca77e0ac..c2c1d631807e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -477,11 +477,11 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(arg) + 1: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: res.add(arg[j-1]) else: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt var start = i while i < frmt.len: if frmt[i] != c: inc(i) @@ -847,7 +847,7 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = # Make sure the index refers to one of the generic params of the type. # XXX: we should catch this earlier and report it as a semantic error. if idx >= typ.len: - doAssert false, "invalid apostrophe type parameter index" + raiseAssert "invalid apostrophe type parameter index" result = typ[idx] for i in 1..stars: diff --git a/compiler/depends.nim b/compiler/depends.nim index cb462e188858..84e66f78063b 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -42,8 +42,7 @@ proc toNimblePath(s: string, isStdlib: bool): string = let sub = "lib/" var start = s.find(sub) if start < 0: - result = "" - doAssert false + raiseAssert "unreachable" else: start += sub.len let base = s[start..^1] diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 7db4c79e31a0..1459bde45723 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -470,7 +470,7 @@ proc gen(c: var Con; n: PNode) = of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1: gen(c, n[1]) of nkVarSection, nkLetSection: genVarSection(c, n) - of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'" + of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'" else: discard when false: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6a78d8693293..933fe57f6fb5 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -348,7 +348,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if conf.configVars.hasKey("doc.googleAnalytics") and conf.configVars.hasKey("doc.plausibleAnalytics"): - doAssert false, "Either use googleAnalytics or plausibleAnalytics" + raiseAssert "Either use googleAnalytics or plausibleAnalytics" if conf.configVars.hasKey("doc.googleAnalytics"): result.analytics = """ @@ -954,8 +954,7 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string = else: result = "" else: - result = "" - doAssert false + raiseAssert "unreachable" type DocFlags = enum kDefault diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8659d511bf7a..c566a718a8e2 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -218,8 +218,7 @@ proc mapType(typ: PType): TJSTypeKind = of tyProc: result = etyProc of tyCstring: result = etyString of tyConcept, tyIterable: - result = etyNone - doAssert false + raiseAssert "unreachable" proc mapType(p: PProc; typ: PType): TJSTypeKind = result = mapType(typ) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 760ee27b5d08..6ae4173477f8 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -1024,7 +1024,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyAlias, tySink: fillBody(c, lastSon(t), body, x, y) - of tyConcept, tyIterable: doAssert false + of tyConcept, tyIterable: raiseAssert "unreachable" proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; diff --git a/compiler/main.nim b/compiler/main.nim index 836f912bbcbb..7118a253c4d8 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -115,7 +115,7 @@ when not defined(leanCompiler): setPipeLinePass(graph, Docgen2JsonPass) of HtmlExt: setPipeLinePass(graph, Docgen2Pass) - else: doAssert false, $ext + else: raiseAssert $ext compilePipelineProject(graph) proc commandCompileToC(graph: ModuleGraph) = @@ -267,7 +267,7 @@ proc mainCommand*(graph: ModuleGraph) = # and it has added this define implictly, so we must undo that here. # A better solution might be to fix system.nim undefSymbol(conf.symbols, "useNimRtl") - of backendInvalid: doAssert false + of backendInvalid: raiseAssert "unreachable" proc compileToBackend() = customizeForBackend(conf.backend) @@ -277,7 +277,7 @@ proc mainCommand*(graph: ModuleGraph) = of backendCpp: commandCompileToC(graph) of backendObjc: commandCompileToC(graph) of backendJs: commandCompileToJS(graph) - of backendInvalid: doAssert false + of backendInvalid: raiseAssert "unreachable" template docLikeCmd(body) = when defined(leanCompiler): diff --git a/compiler/nim.nim b/compiler/nim.nim index d05f01c42717..d0aa888c4146 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -140,7 +140,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = # tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell cmdPrefix.add " --unhandled-rejections=strict" - else: doAssert false, $conf.backend + else: raiseAssert $conf.backend if cmdPrefix.len > 0: cmdPrefix.add " " # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic: # `The parameter is incorrect` diff --git a/compiler/options.nim b/compiler/options.nim index d8ae29eac4f4..bda86a598953 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -663,7 +663,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = template quitOrRaise*(conf: ConfigRef, msg = "") = # xxx in future work, consider whether to also intercept `msgQuit` calls if conf.isDefined("nimDebug"): - doAssert false, msg + raiseAssert msg else: quit(msg) # quits with QuitFailure diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index 7bde76d5f4fe..8517cd942658 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -44,8 +44,7 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): of EvalPass, InterpreterPass: result = interpreterCode(bModule, semNode) of NonePass: - result = nil - doAssert false, "use setPipeLinePass to set a proper PipelinePass" + raiseAssert "use setPipeLinePass to set a proper PipelinePass" proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind, m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator, @@ -133,8 +132,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator of SemPass: nil of NonePass: - doAssert false, "use setPipeLinePass to set a proper PipelinePass" - nil + raiseAssert "use setPipeLinePass to set a proper PipelinePass" if stream == nil: let filename = toFullPathConsiderDirty(graph.config, fileIdx) @@ -208,7 +206,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator when not defined(leanCompiler): discard closeJson(graph, bModule, finalNode) of NonePass: - doAssert false, "use setPipeLinePass to set a proper PipelinePass" + raiseAssert "use setPipeLinePass to set a proper PipelinePass" if graph.config.backend notin {backendC, backendCpp, backendObjc}: # We only write rod files here if no C-like backend is active. diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 7fec1a30ad22..e0d5aa0d378e 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -76,7 +76,7 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(args) + 1: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: result.add(args[j-1]) of '{': @@ -88,10 +88,10 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = num = j if frmt[i] == '}': inc(i) else: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt if j > high(args) + 1: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: result.add(args[j-1]) of 'n': @@ -101,7 +101,7 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = result.add("\n") inc(i) else: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: result.add(frmt[i]) inc(i) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 46751fe4efe8..1a686fd20f45 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -240,7 +240,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; of gcAtomicArc: defineSymbol(conf.symbols, "gcatomicarc") else: - doAssert false, "unreachable" + raiseAssert "unreachable" # ensure we load 'system.nim' again for the real non-config stuff! resetSystemArtifacts(graph) diff --git a/compiler/sem.nim b/compiler/sem.nim index 73422618d3a7..42e631358157 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -594,7 +594,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, ch asgnExpr.typ = recNode.typ result.add newTree(nkExprColonExpr, recNode, asgnExpr) else: - doAssert false + raiseAssert "unreachable" proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] = result = @[] @@ -630,7 +630,7 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: asgnExpr.flags.incl nfSkipFieldChecking result.add newTree(nkExprColonExpr, recNode, asgnExpr) else: - doAssert false + raiseAssert "unreachable" proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode = let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index a60bfee2a9be..c6dec09a9ba7 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -408,8 +408,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P result = a result.typ = n.typ else: - result = nil - doAssert false, $srcTyp.kind + raiseAssert $srcTyp.kind of tyInt..tyInt64, tyUInt..tyUInt64: case srcTyp.kind of tyFloat..tyFloat64: diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index 83c891ca8b71..9ee8516c4739 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -14,7 +14,7 @@ const tinyPrefix = "dist/nim-tinyc-archive".unixToNativePath const nimRoot = currentSourcePath.parentDir.parentDir const tinycRoot = nimRoot / tinyPrefix when not dirExists(tinycRoot): - static: doAssert false, $(tinycRoot, "requires: ./koch installdeps tinyc") + static: raiseAssert $(tinycRoot, "requires: ./koch installdeps tinyc") {.compile: tinycRoot / "tinyc/libtcc.c".} var diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim index 2353feba8c1f..394fb838b385 100644 --- a/compiler/vmconv.nim +++ b/compiler/vmconv.nim @@ -16,7 +16,7 @@ proc fromLit*(a: PNode, T: typedesc): auto = for ai in a: result.incl Ti(ai.intVal) else: - static: doAssert false, "not yet supported: " & $T # add as needed + static: raiseAssert "not yet supported: " & $T # add as needed proc toLit*[T](a: T): PNode = ## generic type => PNode @@ -43,7 +43,7 @@ proc toLit*[T](a: T): PNode = reti.add ai.toLit result.add reti else: - static: doAssert false, "not yet supported: " & $T # add as needed + static: raiseAssert "not yet supported: " & $T # add as needed proc toTimeLit*(a: Time, c: PCtx, obj: PNode, info: TLineInfo): PNode = # probably refactor it into `toLit` in the future diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 49ac7533b177..8aaac6272c9f 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -366,7 +366,7 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) = slotTempFloat, slotTempStr, slotTempComplex}: - doAssert false, "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind + raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind c.prc.regInfo[i] = (inUse: false, kind: slotEmpty) c.clearDest(n, dest) @@ -1073,7 +1073,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case n[1].typ.skipTypes(abstractVarRange).kind of tyString: genUnaryABI(c, n, dest, opcLenStr) of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) - else: doAssert false, $n[1].typ.kind + else: raiseAssert $n[1].typ.kind of mSlice: var d = c.genx(n[1]) diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 7d9e66104cc1..2d7ad63e799b 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -69,9 +69,7 @@ proc getVar*(a: VmArgs; i: Natural): PNode = case p.kind of rkRegisterAddr: result = p.regAddr.node of rkNodeAddr: result = p.nodeAddr[] - else: - result = nil - doAssert false, $p.kind + else: raiseAssert $p.kind proc getNodeAddr*(a: VmArgs; i: Natural): PNode = let nodeAddr = getX(rkNodeAddr, nodeAddr) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 73d24a273331..e81822ba6411 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -243,7 +243,7 @@ proc registerAdditionalOps*(c: PCtx) = case n of 1: setResult(a, round(getFloat(a, 0))) of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int)) - else: doAssert false, $n + else: raiseAssert $n proc `mod Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1))) From faf1c91e6a418e21d56ac6e45e1dbc851f9ffd22 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 11 Aug 2023 00:04:29 +0800 Subject: [PATCH 158/347] fixes move sideeffects issues [backport] (#22439) * fixes move sideeffects issues [backport] * fix openarray * fixes openarray --- compiler/ccgexprs.nim | 18 +++++++++++++++--- tests/destructor/tmove.nim | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 tests/destructor/tmove.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index dd47d1d1f885..64bd16f84f84 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2310,9 +2310,21 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = if op == nil: resetLoc(p, a) else: - let addrExp = makeAddr(n[1], p.module.idgen) - let wasMovedCall = newTreeI(nkCall, n.info, newSymNode(op), addrExp) - genCall(p, wasMovedCall, d) + var b = initLocExpr(p, newSymNode(op)) + case skipTypes(a.t, abstractVar+{tyStatic}).kind + of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for + # openarrays, but it probably shouldn't? + var s: string + if reifiedOpenArray(a.lode): + if a.t.kind in {tyVar, tyLent}: + s = "$1->Field0, $1->Field1" % [rdLoc(a)] + else: + s = "$1.Field0, $1.Field1" % [rdLoc(a)] + else: + s = "$1, $1Len_0" % [rdLoc(a)] + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s]) + else: + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)]) else: let flags = if not canMove(p, n[1], d): {needToCopy} else: {} genAssignment(p, d, a, flags) diff --git a/tests/destructor/tmove.nim b/tests/destructor/tmove.nim new file mode 100644 index 000000000000..2762aff90039 --- /dev/null +++ b/tests/destructor/tmove.nim @@ -0,0 +1,18 @@ +discard """ + targets: "c cpp" +""" + +block: + var called = 0 + + proc bar(a: var int): var int = + inc called + result = a + + proc foo = + var a = 2 + var s = move bar(a) + doAssert called == 1 + doAssert s == 2 + + foo() From 0bf286583ab5260a2d57def0bf6dd41c5fcf76c1 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 11 Aug 2023 12:50:41 +0800 Subject: [PATCH 159/347] `initNodeTable` and friends now return (#22444) --- compiler/ast.nim | 38 +++++++++++++++----------------------- compiler/cgen.nim | 2 +- compiler/docgen.nim | 2 +- compiler/evaltempl.nim | 2 +- compiler/lookups.nim | 4 ++-- compiler/magicsys.nim | 6 +++--- compiler/modulegraphs.nim | 20 ++++++++++---------- compiler/sem.nim | 2 +- compiler/semdata.nim | 10 +++++----- compiler/semexprs.nim | 3 +-- compiler/seminst.nim | 9 ++++----- compiler/semtypes.nim | 3 +-- compiler/semtypinst.nim | 6 +++--- compiler/sigmatch.nim | 4 ++-- compiler/transf.nim | 4 +--- 15 files changed, 51 insertions(+), 64 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 99647e2930c3..35e334b6ab98 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1611,21 +1611,13 @@ proc createModuleAlias*(s: PSym, idgen: IdGenerator, newIdent: PIdent, info: TLi result.loc = s.loc result.annex = s.annex -proc initStrTable*(x: var TStrTable) = - x.counter = 0 - newSeq(x.data, StartSize) - -proc newStrTable*: TStrTable = - result = default(TStrTable) - initStrTable(result) - -proc initIdTable*(x: var TIdTable) = - x.counter = 0 - newSeq(x.data, StartSize) +proc initStrTable*(): TStrTable = + result = TStrTable(counter: 0) + newSeq(result.data, StartSize) -proc newIdTable*: TIdTable = - result = default(TIdTable) - initIdTable(result) +proc initIdTable*(): TIdTable = + result = TIdTable(counter: 0) + newSeq(result.data, StartSize) proc resetIdTable*(x: var TIdTable) = x.counter = 0 @@ -1633,17 +1625,17 @@ proc resetIdTable*(x: var TIdTable) = setLen(x.data, 0) setLen(x.data, StartSize) -proc initObjectSet*(x: var TObjectSet) = - x.counter = 0 - newSeq(x.data, StartSize) +proc initObjectSet*(): TObjectSet = + result = TObjectSet(counter: 0) + newSeq(result.data, StartSize) -proc initIdNodeTable*(x: var TIdNodeTable) = - x.counter = 0 - newSeq(x.data, StartSize) +proc initIdNodeTable*(): TIdNodeTable = + result = TIdNodeTable(counter: 0) + newSeq(result.data, StartSize) -proc initNodeTable*(x: var TNodeTable) = - x.counter = 0 - newSeq(x.data, StartSize) +proc initNodeTable*(): TNodeTable = + result = TNodeTable(counter: 0) + newSeq(result.data, StartSize) proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType = result = t diff --git a/compiler/cgen.nim b/compiler/cgen.nim index de15e8ca4f75..5aafc506b2b6 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1981,7 +1981,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule result.preInitProc = newProc(nil, result) result.preInitProc.flags.incl nimErrorFlagDisabled result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated - initNodeTable(result.dataCache) + result.dataCache = initNodeTable() result.typeStack = @[] result.typeNodesName = getTempName(result) result.nimTypesName = getTempName(result) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 933fe57f6fb5..a6f75b6263b2 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -373,7 +373,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.seenSymbols = newStringTable(modeCaseInsensitive) result.id = 100 result.jEntriesFinal = newJArray() - initStrTable result.types + result.types = initStrTable() result.onTestSnippet = proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} = if conf.docCmd == docCmdSkip: return diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index ff5c3115495a..a5d888858f3a 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -187,7 +187,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.genSymOwner = genSymOwner ctx.config = conf ctx.ic = ic - initIdTable(ctx.mapping) + ctx.mapping = initIdTable() ctx.instID = instID[] ctx.idgen = idgen diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 8b9dd71fdc60..3cf759981fa8 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -72,7 +72,7 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym = proc openScope*(c: PContext): PScope {.discardable.} = result = PScope(parent: c.currentScope, - symbols: newStrTable(), + symbols: initStrTable(), depthLevel: c.scopeDepth + 1) c.currentScope = result @@ -404,7 +404,7 @@ proc openShadowScope*(c: PContext) = ## opens a shadow scope, just like any other scope except the depth is the ## same as the parent -- see `isShadowScope`. c.currentScope = PScope(parent: c.currentScope, - symbols: newStrTable(), + symbols: initStrTable(), depthLevel: c.scopeDepth) proc closeShadowScope*(c: PContext) = diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 1b692f5d6257..1a9daa9f2bd7 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -81,8 +81,8 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType = proc resetSysTypes*(g: ModuleGraph) = g.systemModule = nil - initStrTable(g.compilerprocs) - initStrTable(g.exposed) + g.compilerprocs = initStrTable() + g.exposed = initStrTable() for i in low(g.sysTypes)..high(g.sysTypes): g.sysTypes[i] = nil @@ -124,7 +124,7 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) = proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym = strTableGet(g.exposed, getIdent(g.cache, name)) -proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed) +proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable() proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym = case t.kind diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index f9d0578b5ccc..b9833345357d 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -142,7 +142,7 @@ type isFrontend: bool] proc resetForBackend*(g: ModuleGraph) = - initStrTable(g.compilerprocs) + g.compilerprocs = initStrTable() g.typeInstCache.clear() g.procInstCache.clear() for a in mitems(g.attachedOps): @@ -196,8 +196,8 @@ template semtabAll*(g: ModuleGraph, m: PSym): TStrTable = g.ifaces[m.position].interfHidden proc initStrTables*(g: ModuleGraph, m: PSym) = - initStrTable(semtab(g, m)) - initStrTable(semtabAll(g, m)) + semtab(g, m) = initStrTable() + semtabAll(g, m) = initStrTable() proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) = strTableAdd(semtab(g, m), s) @@ -459,7 +459,7 @@ proc initModuleGraphFields(result: ModuleGraph) = # A module ID of -1 means that the symbol is not attached to a module at all, # but to the module graph: result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32) - initStrTable(result.packageSyms) + result.packageSyms = initStrTable() result.deps = initIntSet() result.importDeps = initTable[FileIndex, seq[FileIndex]]() result.ifaces = @[] @@ -469,9 +469,9 @@ proc initModuleGraphFields(result: ModuleGraph) = result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]() result.suggestErrors = initTable[FileIndex, seq[Suggest]]() result.methods = @[] - initStrTable(result.compilerprocs) - initStrTable(result.exposed) - initStrTable(result.packageTypes) + result.compilerprocs = initStrTable() + result.exposed = initStrTable() + result.packageTypes = initStrTable() result.emptyNode = newNode(nkEmpty) result.cacheSeqs = initTable[string, PNode]() result.cacheCounters = initTable[string, BiggestInt]() @@ -488,7 +488,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = initModuleGraphFields(result) proc resetAllModules*(g: ModuleGraph) = - initStrTable(g.packageSyms) + g.packageSyms = initStrTable() g.deps = initIntSet() g.ifaces = @[] g.importStack = @[] @@ -496,8 +496,8 @@ proc resetAllModules*(g: ModuleGraph) = g.usageSym = nil g.owners = @[] g.methods = @[] - initStrTable(g.compilerprocs) - initStrTable(g.exposed) + g.compilerprocs = initStrTable() + g.exposed = initStrTable() initModuleGraphFields(g) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = diff --git a/compiler/sem.nim b/compiler/sem.nim index 42e631358157..f19f273b5daf 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -462,7 +462,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, # e.g. template foo(T: typedesc): seq[T] # We will instantiate the return type here, because # we now know the supplied arguments - var paramTypes = newIdTable() + var paramTypes = initIdTable() for param, value in genericParamsInMacroCall(s, call): idTablePut(paramTypes, param.typ, value.typ) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index e783e2168e62..0446da676080 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -251,7 +251,7 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next proc put*(p: PProcCon; key, val: PSym) = if not p.mappingExists: - initIdTable(p.mapping) + p.mapping = initIdTable() p.mappingExists = true #echo "put into table ", key.info p.mapping.idTablePut(key, val) @@ -317,13 +317,13 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext = result.converters = @[] result.patterns = @[] result.includedFiles = initIntSet() - initStrTable(result.pureEnumFields) - initStrTable(result.userPragmas) + result.pureEnumFields = initStrTable() + result.userPragmas = initStrTable() result.generics = @[] result.unknownIdents = initIntSet() result.cache = graph.cache result.graph = graph - initStrTable(result.signatures) + result.signatures = initStrTable() result.features = graph.config.features if graph.config.symbolFiles != disabledSf: let id = module.position @@ -388,7 +388,7 @@ proc reexportSym*(c: PContext; s: PSym) = proc newLib*(kind: TLibKind): PLib = new(result) - result.kind = kind #initObjectSet(result.syms) + result.kind = kind #result.syms = initObjectSet() proc addToLib*(lib: PLib, sym: PSym) = #if sym.annex != nil and not isGenericRoutine(sym): diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index dca4ce6e0d98..52eef76316a4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2361,8 +2361,7 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType; let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar") if sym == nil: localError(c.config, info, "system needs: nimCreateFlowVar") - var bindings: TIdTable = default(TIdTable) - initIdTable(bindings) + var bindings: TIdTable = initIdTable() bindings.idTablePut(sym.ast[genericParamsPos][0].typ, t) result = c.semGenerateInstance(c, sym, bindings, info) # since it's an instantiation, we unmark it as a compilerproc. Otherwise diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0dc3e3cfc90c..c7735903e030 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -122,8 +122,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = inc c.inGenericInst # add it here, so that recursive generic procs are possible: var b = n[bodyPos] - var symMap: TIdTable - initIdTable symMap + var symMap: TIdTable = initIdTable() if params != nil: for i in 1.. Date: Fri, 11 Aug 2023 17:08:51 +0800 Subject: [PATCH 160/347] modernize lambdalifting (#22449) * modernize lambdalifting * follow @beef331's suggestions --- compiler/lambdalifting.nim | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 37c913fe2e5d..ee1e5b7976bb 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -320,12 +320,10 @@ type idgen: IdGenerator proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass = - result.processed = initIntSet() - result.capturedVars = initIntSet() - result.ownerToType = initTable[int, PType]() - result.processed.incl(fn.id) - result.graph = g - result.idgen = idgen + result = DetectionPass(processed: toIntSet([fn.id]), + capturedVars: initIntSet(), ownerToType: initTable[int, PType](), + graph: g, idgen: idgen + ) discard """ proc outer = @@ -530,9 +528,8 @@ type unownedEnvVars: Table[int, PNode] # only required for --newruntime proc initLiftingPass(fn: PSym): LiftingPass = - result.processed = initIntSet() - result.processed.incl(fn.id) - result.envVars = initTable[int, PNode]() + result = LiftingPass(processed: toIntSet([fn.id]), + envVars: initTable[int, PNode]()) proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = let s = n.sym From 3bb75f2dea1c65ee6b4b7fdca48748c97088cf76 Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 11 Aug 2023 18:50:31 +0800 Subject: [PATCH 161/347] close #18103 internal error: inconsistent environment type (#22451) --- tests/vm/t18103.nim | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/vm/t18103.nim diff --git a/tests/vm/t18103.nim b/tests/vm/t18103.nim new file mode 100644 index 000000000000..8622ab29062d --- /dev/null +++ b/tests/vm/t18103.nim @@ -0,0 +1,35 @@ +discard """ + targets: "c cpp" + matrix: "--mm:refc; --mm:arc" +""" + +import base64, complex, sequtils, math, sugar + +type + + FP = float + T = object + index: int + arg: FP + val: Complex[FP] + M = object + alpha, beta: FP + +func a(s: openArray[T], model: M): seq[T] = + let f = (tn: int) => model.alpha + FP(tn) * model.beta; + return mapIt s: + block: + let s = it.val * rect(1.0, - f(it.index)) + T(index: it.index, arg: phase(s), val: s) + +proc b(): float64 = + var s = toSeq(0..10).mapIt(T(index: it, arg: 1.0, val: complex.complex(1.0))) + discard a(s, M(alpha: 1, beta: 1)) + return 1.0 + +func cc(str: cstring, offset: ptr[cdouble]): cint {.exportc.} = + offset[] = b() + return 0 + +static: + echo b() From 277393d0f1c06c422afb6fae581069960609b730 Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 11 Aug 2023 19:11:47 +0800 Subject: [PATCH 162/347] =?UTF-8?q?close=20#17045;Compiler=20crash=20when?= =?UTF-8?q?=20a=20tuple=20iterator=20with=20when=20nimvm=20is=20=E2=80=A6?= =?UTF-8?q?=20(#22452)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #17045;Compiler crash when a tuple iterator with when nimvm is iterated in a closure iterator --- tests/async/t17045.nim | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/async/t17045.nim diff --git a/tests/async/t17045.nim b/tests/async/t17045.nim new file mode 100644 index 000000000000..2b5acf48a480 --- /dev/null +++ b/tests/async/t17045.nim @@ -0,0 +1,28 @@ +discard """ + targets: "c cpp" + matrix: "--mm:refc; --mm:arc" +""" + +type Future = ref object + +iterator paths: string = + # without "when nimvm" everything works + when nimvm: + yield "test.md" + else: + yield "test.md" + +template await(f: Future): string = + # need this yield, also the template has to return something + yield f + "hello world" + +proc generatePostContextsAsync() = + iterator generatePostContextsAsyncIter(): Future {.closure.} = + for filePath in paths(): + var temp = await Future() + + # need this line + var nameIterVar = generatePostContextsAsyncIter + +generatePostContextsAsync() \ No newline at end of file From 72bc72bf9ea470603420a0b56f63dad063f808a9 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 11 Aug 2023 22:16:58 +0800 Subject: [PATCH 163/347] refactor `result = default(...)` into object construction (#22455) --- compiler/semtypinst.nim | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index aef1e0374840..e3c8c1c0a727 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -96,9 +96,7 @@ proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable = copyIdTable(result.topLayer, pt) proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = - result = LayeredIdTable() - result.nextLayer = cl.typeMap - result.topLayer = initIdTable() + result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initIdTable()) proc lookup(typeMap: LayeredIdTable, key: PType): PType = result = nil @@ -685,13 +683,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = - result = default(TReplTypeVars) - result.symMap = initIdTable() - result.localCache = initIdTable() - result.typeMap = typeMap - result.info = info - result.c = p - result.owner = owner + result = TReplTypeVars(symMap: initIdTable(), + localCache: initIdTable(), typeMap: typeMap, + info: info, c: p, owner: owner) proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; owner: PSym, allowMetaTypes = false, From 469c9cfab487380ba85520c90a2fad7d658c3023 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 11 Aug 2023 22:18:24 +0800 Subject: [PATCH 164/347] unpublic the sons field of PType; the precursor to PType refactorings (#22446) * unpublic the sons field of PType * tiny fixes * fixes an omittance * fixes IC * fixes --- compiler/ast.nim | 22 +++++++++++++++++++--- compiler/ccgexprs.nim | 4 ++-- compiler/ccgtypes.nim | 4 ++-- compiler/concepts.nim | 6 +++--- compiler/docgen.nim | 4 ++-- compiler/ic/ic.nim | 4 ++-- compiler/liftdestructors.nim | 2 +- compiler/magicsys.nim | 2 +- compiler/pragmas.nim | 4 ++-- compiler/semcall.nim | 5 ++--- compiler/semdata.nim | 30 +++++++++++++++--------------- compiler/semexprs.nim | 2 +- compiler/sempass2.nim | 2 +- compiler/semstmts.nim | 14 +++++++------- compiler/semtypes.nim | 18 ++++++++++++------ compiler/semtypinst.nim | 10 ++++------ compiler/sighashes.nim | 2 +- compiler/sigmatch.nim | 19 +++++++++---------- compiler/types.nim | 10 +++++----- compiler/varpartitions.nim | 4 ++-- compiler/vmdeps.nim | 2 +- 21 files changed, 94 insertions(+), 76 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 35e334b6ab98..ce5ad0f292a0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -958,7 +958,7 @@ type kind*: TTypeKind # kind of type callConv*: TCallingConvention # for procs flags*: TTypeFlags # flags of the type - sons*: TTypeSeq # base types, etc. + sons: TTypeSeq # base types, etc. n*: PNode # node for types: # for range types a nkRange node # for record types a nkRecord node @@ -1537,15 +1537,31 @@ proc `$`*(s: PSym): string = else: result = "" -proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType = +iterator items*(t: PType): PType = + for i in 0.. 0: result.add ", " - getDefaultValue(p, t.sons[1], info, result) + getDefaultValue(p, t[1], info, result) result.add "}" #result = rope"{}" of tyOpenArray, tyVarargs: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c2c1d631807e..eb5e906e425f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1178,9 +1178,9 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = var memberOp = "#." #only virtual var typ: PType if isCtor: - typ = prc.typ.sons[0] + typ = prc.typ[0] else: - typ = prc.typ.sons[1] + typ = prc.typ[1] if typ.kind == tyPtr: typ = typ[0] memberOp = "#->" diff --git a/compiler/concepts.nim b/compiler/concepts.nim index c3d8d265d86f..681e4eace90f 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -167,9 +167,9 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = # modifiers in the concept must be there in the actual implementation # too but not vice versa. if a.kind == f.kind: - result = matchType(c, f.sons[0], a.sons[0], m) + result = matchType(c, f[0], a[0], m) elif m.magic == mArrPut: - result = matchType(c, f.sons[0], a, m) + result = matchType(c, f[0], a, m) else: result = false of tyEnum, tyObject, tyDistinct: @@ -264,7 +264,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool = m.inferred.setLen oldLen return false - if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m): + if not matchReturnType(c, n[0].sym.typ[0], candidate.typ[0], m): m.inferred.setLen oldLen return false diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a6f75b6263b2..7edee1663f6b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1196,9 +1196,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): result.json["signature"]["genericParams"] = newJArray() for genericParam in n[genericParamsPos]: var param = %{"name": %($genericParam)} - if genericParam.sym.typ.sons.len > 0: + if genericParam.sym.typ.len > 0: param["types"] = newJArray() - for kind in genericParam.sym.typ.sons: + for kind in genericParam.sym.typ: param["types"].add %($kind) result.json["signature"]["genericParams"].add param if optGenIndex in d.conf.globalOptions: diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index c2f3f793c376..845e3d1fa6f9 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -359,7 +359,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI paddingAtEnd: t.paddingAtEnd) storeNode(p, t, n) p.typeInst = t.typeInst.storeType(c, m) - for kid in items t.sons: + for kid in items t: p.types.add kid.storeType(c, m) c.addMissing t.sym p.sym = t.sym.safeItemId(c, m) @@ -917,7 +917,7 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; result.attachedOps[op] = loadSym(c, g, si, item) result.typeInst = loadType(c, g, si, t.typeInst) for son in items t.types: - result.sons.add loadType(c, g, si, son) + result.addSon loadType(c, g, si, son) loadAstBody(t, n) when false: for gen, id in items t.methods: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 6ae4173477f8..f28586addb4a 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -302,7 +302,7 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode = result.add newSymNode(op) if sfNeverRaises notin op.flags: c.canRaise = true - if op.typ.sons[1].kind == tyVar: + if op.typ[1].kind == tyVar: result.add genAddr(c, x) else: result.add x diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 1a9daa9f2bd7..bb8c6a5b98c8 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -100,7 +100,7 @@ proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} = proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) = let s = son.skipIntLit(id) - father.sons.add(s) + father.add(s) propagateToOwner(father, s) proc getCompilerProc*(g: ModuleGraph; name: string): PSym = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 56e25c0b43d8..6a371ba06797 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -140,9 +140,9 @@ proc pragmaEnsures(c: PContext, n: PNode) = else: openScope(c) let o = getCurrOwner(c) - if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil: + if o.kind in routineKinds and o.typ != nil and o.typ[0] != nil: var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info) - s.typ = o.typ.sons[0] + s.typ = o.typ[0] incl(s.flags, sfUsed) addDecl(c, s) n[1] = c.semExpr(c, n[1]) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 073c00c37e22..e7c9226dd2b3 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -598,7 +598,7 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = # nested, add all the types to stack let startIdx = if u.kind in ConcreteTypes: 0 else: 1 - endIdx = min(u.sons.len() - startIdx, t.sons.len()) + endIdx = min(u.len() - startIdx, t.len()) for i in startIdx ..< endIdx: # early exit with current impl @@ -717,8 +717,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = if formal.kind == tyStatic and arg.kind != tyStatic: let evaluated = c.semTryConstExpr(c, n[i]) if evaluated != nil: - arg = newTypeS(tyStatic, c) - arg.sons = @[evaluated.typ] + arg = newTypeS(tyStatic, c, sons = @[evaluated.typ]) arg.n = evaluated let tm = typeRel(m, formal, arg) if tm in {isNone, isConvertible}: return nil diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 0446da676080..db3b8370ee56 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -395,8 +395,8 @@ proc addToLib*(lib: PLib, sym: PSym) = # LocalError(sym.info, errInvalidPragma) sym.annex = lib -proc newTypeS*(kind: TTypeKind, c: PContext): PType = - result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c)) +proc newTypeS*(kind: TTypeKind, c: PContext, sons: seq[PType] = @[]): PType = + result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c), sons = sons) proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType = result = newType(tyPtr, nextTypeId(idgen), owner) @@ -446,13 +446,15 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]; idgen: IdGenerator): PType = - result = newType(kind, nextTypeId(idgen), owner) - result.sons = sons + result = newType(kind, nextTypeId(idgen), owner, sons = sons) proc newTypeWithSons*(c: PContext, kind: TTypeKind, sons: seq[PType]): PType = - result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c)) - result.sons = sons + result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c), sons = sons) + +proc newTypeWithSons*(c: PContext, kind: TTypeKind, + parent: PType): PType = + result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c), parent = parent) proc makeStaticExpr*(c: PContext, n: PNode): PNode = result = newNodeI(nkStaticExpr, n.info) @@ -461,21 +463,21 @@ proc makeStaticExpr*(c: PContext, n: PNode): PNode = else: newTypeWithSons(c, tyStatic, @[n.typ]) proc makeAndType*(c: PContext, t1, t2: PType): PType = - result = newTypeS(tyAnd, c) - result.sons = @[t1, t2] + result = newTypeS(tyAnd, c, sons = @[t1, t2]) propagateToOwner(result, t1) propagateToOwner(result, t2) result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) result.flags.incl tfHasMeta proc makeOrType*(c: PContext, t1, t2: PType): PType = - result = newTypeS(tyOr, c) + if t1.kind != tyOr and t2.kind != tyOr: - result.sons = @[t1, t2] + result = newTypeS(tyOr, c, sons = @[t1, t2]) else: + result = newTypeS(tyOr, c) template addOr(t1) = if t1.kind == tyOr: - for x in t1.sons: result.rawAddSon x + for x in t1: result.rawAddSon x else: result.rawAddSon t1 addOr(t1) @@ -486,8 +488,7 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType = result.flags.incl tfHasMeta proc makeNotType*(c: PContext, t1: PType): PType = - result = newTypeS(tyNot, c) - result.sons = @[t1] + result = newTypeS(tyNot, c, sons = @[t1]) propagateToOwner(result, t1) result.flags.incl(t1.flags * {tfHasStatic}) result.flags.incl tfHasMeta @@ -498,8 +499,7 @@ proc nMinusOne(c: PContext; n: PNode): PNode = # Remember to fix the procs below this one when you make changes! proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = let intType = getSysType(c.graph, n.info, tyInt) - result = newTypeS(tyRange, c) - result.sons = @[intType] + result = newTypeS(tyRange, c, sons = @[intType]) if n.typ != nil and n.typ.n == nil: result.flags.incl tfUnresolved result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType), diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 52eef76316a4..df65b337125d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1422,7 +1422,7 @@ proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode = while ty != nil: f = getSymFromList(ty.n, i) if f != nil: break - ty = ty.sons[0] # enum inheritance + ty = ty[0] # enum inheritance if f != nil: result = newSymNode(f) result.info = n.info diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 0aa8080597ee..854247095e8e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1327,7 +1327,7 @@ proc track(tracked: PEffects, n: PNode) = proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool = if spec.typ.kind == tyOr: - for t in spec.typ.sons: + for t in spec.typ: if safeInheritanceDiff(g.excType(real), t) <= 0: return true else: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e64cd8db63e7..39c37f4fb25f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -45,7 +45,7 @@ proc hasEmpty(typ: PType): bool = result = typ.lastSon.kind == tyEmpty elif typ.kind == tyTuple: result = false - for s in typ.sons: + for s in typ: result = result or hasEmpty(s) else: result = false @@ -1344,7 +1344,7 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) = of tyArray: return traverseSubTypes(c, t[1]) of tyProc: - for subType in t.sons: + for subType in t: if subType != nil: subresult traverseSubTypes(c, subType) if result: @@ -1377,7 +1377,7 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) = of tyUserTypeClass, tyUserTypeClassInst: error("non-invariant type parameters are not supported in concepts") of tyTuple: - for fieldType in t.sons: + for fieldType in t: subresult traverseSubTypes(c, fieldType) of tyPtr, tyRef, tyVar, tyLent: if t.base.kind == tyGenericParam: return true @@ -2283,18 +2283,18 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, let isCtor = sfConstructor in s.flags let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member" if c.config.backend == backendCpp: - if s.typ.sons.len < 2 and not isCtor: + if s.typ.len < 2 and not isCtor: localError(c.config, n.info, pragmaName & " must have at least one parameter") - for son in s.typ.sons: + for son in s.typ: if son!=nil and son.isMetaType: localError(c.config, n.info, pragmaName & " unsupported for generic routine") var typ: PType if isCtor: - typ = s.typ.sons[0] + typ = s.typ[0] if typ == nil or typ.kind != tyObject: localError(c.config, n.info, "constructor must return an object") else: - typ = s.typ.sons[1] + typ = s.typ[1] if typ.kind == tyPtr and not isCtor: typ = typ[0] if typ.kind != tyObject: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index fcab8f1c9836..8e304288b6d1 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -38,13 +38,20 @@ const errNoGenericParamsAllowedForX = "no generic parameters allowed for $1" errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types" +proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext, sons: seq[PType]): PType = + if prev == nil or prev.kind == tyGenericBody: + result = newTypeS(kind, c, sons = sons) + else: + result = newType(prev, sons) + if result.kind == tyForward: result.kind = kind + #if kind == tyError: result.flags.incl tfCheckedForDestructor + proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if prev == nil or prev.kind == tyGenericBody: result = newTypeS(kind, c) else: result = prev if result.kind == tyForward: result.kind = kind - #if kind == tyError: result.flags.incl tfCheckedForDestructor proc newConstraint(c: PContext, k: TTypeKind): PType = result = newTypeS(tyBuiltInTypeClass, c) @@ -245,7 +252,7 @@ proc isRecursiveType*(t: PType): bool = proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdGenerator) = let s = son.skipIntLit(id) - father.sons.add(s) + father.add(s) if isRecursiveType(s): localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'") else: @@ -1012,7 +1019,7 @@ proc findEnforcedStaticType(t: PType): PType = if t == nil: return nil if t.kind == tyStatic: return t if t.kind == tyAnd: - for s in t.sons: + for s in t: let t = findEnforcedStaticType(s) if t != nil: return t @@ -1658,11 +1665,10 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = pragmas = n[1] inherited = n[2] - result = newOrPrevType(tyUserTypeClass, prev, c) - result.flags.incl tfCheckedForDestructor var owner = getCurrOwner(c) var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType], c.idgen) - result.sons = @[candidateTypeSlot] + result = newOrPrevType(tyUserTypeClass, prev, c, sons = @[candidateTypeSlot]) + result.flags.incl tfCheckedForDestructor result.n = n if inherited.kind != nkEmpty: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index e3c8c1c0a727..56b922fdafd2 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -52,7 +52,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = continue block matchType: - for j in 1..high(key.sons): + for j in 1.. 0: + if t.len > 0: c.hashType t.lastSon, flags, conf if tfVarIsPtr in t.flags: c &= ".varisptr" of tyFromExpr: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 199d8bae98cb..048b74547f9d 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -228,7 +228,7 @@ proc sumGeneric(t: PType): int = inc result of tyOr: var maxBranch = 0 - for branch in t.sons: + for branch in t: let branchSum = sumGeneric(branch) if branchSum > maxBranch: maxBranch = branchSum inc result, maxBranch @@ -904,7 +904,7 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = else: discard elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: - var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons) + var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ) inferred.n = newIntNode(nkIntLit, rhs) put(c, lhs.typ, inferred) if c.c.matchedConcept != nil: @@ -1109,7 +1109,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # both int and string must match against number # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric - for branch in a.sons: + for branch in a: let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x @@ -1120,7 +1120,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.typedescMatched = true # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough - for branch in a.sons: + for branch in a: let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x @@ -1622,7 +1622,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyAnd: considerPreviousT: result = isEqual - for branch in f.sons: + for branch in f: let x = typeRel(c, branch, aOrig, flags) if x < isSubtype: return isNone # 'and' implies minimum matching result: @@ -1635,7 +1635,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone let oldInheritancePenalty = c.inheritancePenalty var maxInheritance = 0 - for branch in f.sons: + for branch in f: c.inheritancePenalty = 0 let x = typeRel(c, branch, aOrig, flags) maxInheritance = max(maxInheritance, c.inheritancePenalty) @@ -1650,7 +1650,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyNot: considerPreviousT: - for branch in f.sons: + for branch in f: if typeRel(c, branch, aOrig, flags) != isNone: return isNone @@ -2121,8 +2121,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if evaluated != nil: # Don't build the type in-place because `evaluated` and `arg` may point # to the same object and we'd end up creating recursive types (#9255) - let typ = newTypeS(tyStatic, c) - typ.sons = @[evaluated.typ] + let typ = newTypeS(tyStatic, c, sons = @[evaluated.typ]) typ.n = evaluated arg = copyTree(arg) # fix #12864 arg.typ = typ @@ -2714,7 +2713,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = # forget all inferred types if the overload matching failed if m.state == csNoMatch: for t in m.inferredTypes: - if t.len > 1: t.sons.setLen 1 + if t.len > 1: t.newSons 1 proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool = var m = newCandidate(c, f) diff --git a/compiler/types.nim b/compiler/types.nim index a385a291fe27..3160583787e4 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -566,7 +566,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if t.kind == tyGenericParam and t.len > 0: result.add ": " var first = true - for son in t.sons: + for son in t: if not first: result.add " or " result.add son.typeToString first = false @@ -637,14 +637,14 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result.add(typeToString(t[i])) result.add "]" of tyAnd: - for i, son in t.sons: + for i, son in t: result.add(typeToString(son)) - if i < t.sons.high: + if i < t.len - 1: result.add(" and ") of tyOr: - for i, son in t.sons: + for i, son in t: result.add(typeToString(son)) - if i < t.sons.high: + if i < t.len - 1: result.add(" or ") of tyNot: result = "not " & typeToString(t[0]) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 4dd51b63bce8..74bf63da8ff3 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -407,8 +407,8 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = if typ != nil and i < typ.len: assert(typ.n[i].kind == nkSym) let paramType = typ.n[i].typ - if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and - canAlias(paramType, typ.sons[0]): + if not paramType.isCompileTimeOnly and not typ[0].isEmptyType and + canAlias(paramType, typ[0]): allRoots(it, result, RootEscapes) else: allRoots(it, result, RootEscapes) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 75692fcc0eff..863896419eb0 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -199,7 +199,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; # only named tuples have a node, unnamed tuples don't if t.n.isNil: result = newNodeX(nkTupleConstr) - for subType in t.sons: + for subType in t: result.add mapTypeToAst(subType, info) else: result = newNodeX(nkTupleTy) From 48da472dd2e625d2d794907afd33a4a153fa2dc1 Mon Sep 17 00:00:00 2001 From: Pylgos <43234674+Pylgos@users.noreply.github.com> Date: Sat, 12 Aug 2023 01:23:09 +0900 Subject: [PATCH 165/347] fix #22448 Remove `structuredErrorHook` temporary in `tryConstExpr` (#22450) * fix #22448 * add test --- compiler/sem.nim | 9 +++++++++ nimsuggest/tests/t22448.nim | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 nimsuggest/tests/t22448.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index f19f273b5daf..f69e7a69d05b 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -353,6 +353,11 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = c.config.m.errorOutputs = {} c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here + when defined(nimsuggest): + # Remove the error hook so nimsuggest doesn't report errors there + let tempHook = c.graph.config.structuredErrorHook + c.graph.config.structuredErrorHook = nil + try: result = evalConstExpr(c.module, c.idgen, c.graph, e) if result == nil or result.kind == nkEmpty: @@ -363,6 +368,10 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = except ERecoverableError: result = nil + when defined(nimsuggest): + # Restore the error hook + c.graph.config.structuredErrorHook = tempHook + c.config.errorCounter = oldErrorCount c.config.errorMax = oldErrorMax c.config.m.errorOutputs = oldErrorOutputs diff --git a/nimsuggest/tests/t22448.nim b/nimsuggest/tests/t22448.nim new file mode 100644 index 000000000000..8664bbbc3c7a --- /dev/null +++ b/nimsuggest/tests/t22448.nim @@ -0,0 +1,11 @@ +proc fn(a: static float) = discard +proc fn(a: int) = discard + +let x = 1 +fn(x) + +discard """ +$nimsuggest --tester --v3 $file +>chk $file +chk;;skUnknown;;;;Hint;;* +""" From 3f7e1d7daadf4002da1a155d7b98ff7fcca9e2fa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 12 Aug 2023 00:24:46 +0800 Subject: [PATCH 166/347] replace `doAssert false` with `raiseAssert` in lib, which works better with strictdefs (#22458) --- lib/impure/re.nim | 2 +- lib/js/dom.nim | 6 +++--- lib/packages/docutils/rstgen.nim | 2 +- lib/pure/coro.nim | 4 ++-- lib/pure/hashes.nim | 8 ++++---- lib/pure/osproc.nim | 2 +- lib/pure/parseopt.nim | 2 +- lib/pure/streams.nim | 2 +- lib/pure/strformat.nim | 2 +- lib/pure/times.nim | 2 +- lib/pure/typetraits.nim | 2 +- lib/pure/unittest.nim | 2 +- lib/std/formatfloat.nim | 4 ++-- lib/std/genasts.nim | 2 +- lib/std/jsbigints.nim | 6 +++--- lib/std/jsonutils.nim | 6 +++--- lib/std/private/globs.nim | 2 +- lib/std/private/osfiles.nim | 2 +- lib/std/private/ospaths2.nim | 4 ++-- lib/std/sysrand.nim | 2 +- lib/system.nim | 2 +- testament/lib/stdtest/specialpaths.nim | 2 +- 22 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index f97a31d218d2..5e84091c7748 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -460,7 +460,7 @@ template `=~` *(s: string, pattern: Regex): untyped = elif line =~ re"\s*(\#.*)": # matches a comment # note that the implicit `matches` array is different from 1st branch result = $(matches[0],) - else: doAssert false + else: raiseAssert "unreachable" doAssert not declared(matches) doAssert parse("NAME = LENA") == """("NAME", "LENA")""" doAssert parse(" # comment ... ") == """("# comment ... ",)""" diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 0a825865b319..ceb0375b7c9d 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1423,7 +1423,7 @@ when defined(nodejs): parent.childNodes[i] = newNode return inc i - doAssert false, "old node not in node list" + raiseAssert "old node not in node list" proc removeChild*(parent, child: Node) = child.parentNode = nil @@ -1433,7 +1433,7 @@ when defined(nodejs): parent.childNodes.delete(i) return inc i - doAssert false, "old node not in node list" + raiseAssert "old node not in node list" proc insertBefore*(parent, newNode, before: Node) = appendChild(parent, newNode) @@ -1445,7 +1445,7 @@ when defined(nodejs): parent.childNodes[i-1] = newNode return inc i - #doAssert false, "before not in node list" + #raiseAssert "before not in node list" proc createElement*(d: Document, identifier: cstring): Element = new(result) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 008dff60aa80..f06e11de25fb 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1175,7 +1175,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "
    $1
    ", " $1\n", result) of rnOption, rnOptionString, rnOptionArgument: - doAssert false, "renderRstToOut" + raiseAssert "renderRstToOut" of rnLiteralBlock: renderAux(d, n, "$1\n", "\n\n$2\\begin{rstpre}\n$1\n\\end{rstpre}\n\n", result) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 7f0f551e60ba..5cdcb75fe70a 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -224,7 +224,7 @@ proc switchTo(current, to: CoroutinePtr) = elif to.state == CORO_CREATED: # Coroutine is started. coroExecWithStack(runCurrentTask, to.stack.bottom) - #doAssert false + #raiseAssert "unreachable" else: {.error: "Invalid coroutine backend set.".} # Execution was just resumed. Restore frame information and set active stack. @@ -266,7 +266,7 @@ proc runCurrentTask() = current.state = CORO_FINISHED nimGC_setStackBottom(ctx.ncbottom) suspend(0) # Exit coroutine without returning from coroExecWithStack() - doAssert false + raiseAssert "unreachable" proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discardable.} = ## Schedule coroutine for execution. It does not run immediately. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index ad164d6d3110..6d246d5b9beb 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -368,16 +368,16 @@ proc murmurHash(x: openArray[byte]): Hash = return cast[Hash](h1) proc hashVmImpl(x: cstring, sPos, ePos: int): Hash = - doAssert false, "implementation override in compiler/vmops.nim" + raiseAssert "implementation override in compiler/vmops.nim" proc hashVmImpl(x: string, sPos, ePos: int): Hash = - doAssert false, "implementation override in compiler/vmops.nim" + raiseAssert "implementation override in compiler/vmops.nim" proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash = - doAssert false, "implementation override in compiler/vmops.nim" + raiseAssert "implementation override in compiler/vmops.nim" proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash = - doAssert false, "implementation override in compiler/vmops.nim" + raiseAssert "implementation override in compiler/vmops.nim" proc hash*(x: string): Hash = ## Efficient hashing of strings. diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index e30f1da737ad..91c45e0539f3 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1348,7 +1348,7 @@ elif not defined(useNimRtl): p.exitStatus = status break else: - doAssert false, "unreachable!" + raiseAssert "unreachable!" result = exitStatusLikeShell(p.exitStatus) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 059f8e0f5d3c..2d039f1e4ad1 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -251,7 +251,7 @@ proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {}, else: # we cannot provide this for NimRtl creation on Posix, because we can't # access the command line arguments then! - doAssert false, "empty command line given but" & + raiseAssert "empty command line given but" & " real command line is not accessible" result.kind = cmdEnd result.key = "" diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 003f8ca4c233..e18e2d43a65d 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1530,7 +1530,7 @@ when false: of fmReadWrite: flags = O_RDWR or int(O_CREAT) of fmReadWriteExisting: flags = O_RDWR of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND - static: doAssert false # handle bug #17888 + static: raiseAssert "unreachable" # handle bug #17888 var handle = open(filename, flags) if handle < 0: raise newEOS("posix.open() call failed") result = newFileHandleStream(handle) diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 3fedff07b575..1cebefee102c 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -663,7 +663,7 @@ proc strformatImpl(f: string; openChar, closeChar: char, strlit.add closeChar inc i, 2 else: - doAssert false, "invalid format string: '$1' instead of '$1$1'" % $closeChar + raiseAssert "invalid format string: '$1' instead of '$1$1'" % $closeChar inc i else: strlit.add f[i] diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 4f7af657cf79..f5775e4d951a 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -2089,7 +2089,7 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, i.inc 2 else: result = false - of Lit: doAssert false, "Can't happen" + of Lit: raiseAssert "Can't happen" proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, input: string): DateTime = diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index c20f9e64519c..70eb1b81c0c4 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -225,7 +225,7 @@ macro genericParamsImpl(T: typedesc): untyped = case ai.typeKind of ntyTypeDesc: ret = ai - of ntyStatic: doAssert false + of ntyStatic: raiseAssert "unreachable" else: # getType from a resolved symbol might return a typedesc symbol. # If so, use it directly instead of wrapping it in StaticParam. diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index afe98ca4e2ad..3b3684789b91 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -235,7 +235,7 @@ proc colorOutput(): bool = else: result = false of "on": result = true of "off": result = false - else: doAssert false, $color + else: raiseAssert $color when declared(stdout): if existsEnv("NIMTEST_COLOR"): diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index b216d1fd0671..48973aa5583a 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -86,7 +86,7 @@ proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32 proc addFloatRoundtrip*(result: var string; x: float | float32) = when nimvm: - doAssert false + raiseAssert "unreachable" else: var buffer {.noinit.}: array[65, char] let n = writeFloatToBufferRoundtrip(buffer, x) @@ -94,7 +94,7 @@ proc addFloatRoundtrip*(result: var string; x: float | float32) = proc addFloatSprintf*(result: var string; x: float) = when nimvm: - doAssert false + raiseAssert "unreachable" else: var buffer {.noinit.}: array[65, char] let n = writeFloatToBufferSprintf(buffer, x) diff --git a/lib/std/genasts.nim b/lib/std/genasts.nim index 05b2823efccd..04257533dbab 100644 --- a/lib/std/genasts.nim +++ b/lib/std/genasts.nim @@ -24,7 +24,7 @@ macro genAstOpt*(options: static set[GenAstOpt], args: varargs[untyped]): untype result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]): # each local symbol we access must be explicitly captured if not cond: - doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] + raiseAssert "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] let a = 3 check2 a*2 == a+3 if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4' diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim index 067de78b5ba7..4e996ea7b904 100644 --- a/lib/std/jsbigints.nim +++ b/lib/std/jsbigints.nim @@ -14,7 +14,7 @@ func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} = runnableExamples: doAssert big(1234567890) == big"1234567890" doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} = ## Constructor for `JsBigInt`. @@ -28,11 +28,11 @@ func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} = doAssert 0xdeadbeaf'big == 0xdeadbeaf.big doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big doAssert not compiles(static(12'big)) - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} = ## Alias for `'big` - when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} = ## Converts from `JsBigInt` to `cstring` representation. diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 847761e2f8a5..b1025d24b308 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -95,7 +95,7 @@ macro getDiscriminants(a: typedesc): seq[string] = result = quote do: seq[string].default else: - doAssert false, "unexpected kind: " & $t2.kind + raiseAssert "unexpected kind: " & $t2.kind macro initCaseObject(T: typedesc, fun: untyped): untyped = ## does the minimum to construct a valid case object, only initializing @@ -109,7 +109,7 @@ macro initCaseObject(T: typedesc, fun: untyped): untyped = case t.kind of nnkObjectTy: t2 = t[2] of nnkRefTy: t2 = t[0].getTypeImpl[2] - else: doAssert false, $t.kind # xxx `nnkPtrTy` could be handled too + else: raiseAssert $t.kind # xxx `nnkPtrTy` could be handled too doAssert t2.kind == nnkRecList result = newTree(nnkObjConstr) result.add sym @@ -289,7 +289,7 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = i.inc else: # checkJson not appropriate here - static: doAssert false, "not yet implemented: " & $T + static: raiseAssert "not yet implemented: " & $T proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T = ## reverse of `toJson` diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index 5e3e33cb4cb7..64065aac8136 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -60,7 +60,7 @@ proc nativeToUnixPath*(path: string): string = result[0] = '/' result[1] = path[0] if path.len > 2 and path[2] != '\\': - doAssert false, "paths like `C:foo` are currently unsupported, path: " & path + raiseAssert "paths like `C:foo` are currently unsupported, path: " & path when DirSep == '\\': result = replace(result, '\\', '/') diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index 78afd35dacac..f2e7bf11d2fe 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -396,7 +396,7 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", if not tryMoveFSObject(source, dest, isDir = false): when defined(windows): - doAssert false + raiseAssert "unreachable" else: # Fallback to copy & del copyFile(source, dest, {cfSymlinkAsIs}) diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 18a01b10495b..421def62b399 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -259,7 +259,7 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs result = path[0] == '/' else: - doAssert false # if ever hits here, adapt as needed + raiseAssert "unreachable" # if ever hits here, adapt as needed when FileSystemCaseSensitive: template `!=?`(a, b: char): bool = a != b @@ -859,7 +859,7 @@ when not defined(nimscript): {.emit: "`ret` = process.cwd();".} return $ret elif defined(js): - doAssert false, "use -d:nodejs to have `getCurrentDir` defined" + raiseAssert "use -d:nodejs to have `getCurrentDir` defined" elif defined(windows): var bufsize = MAX_PATH.int32 var res = newWideCString("", bufsize) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 7943f2e1ba3e..8526336ad32a 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -192,7 +192,7 @@ elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): while result < size: let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int if readBytes == 0: - doAssert false + raiseAssert "unreachable" elif readBytes > 0: inc(result, readBytes) else: diff --git a/lib/system.nim b/lib/system.nim index 3076fe2fdae7..521380a57838 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2289,7 +2289,7 @@ elif defined(nimdoc): ## `quit(int(0x100000000))` is equal to `quit(127)` on Linux. ## ## .. danger:: In almost all cases, in particular in library code, prefer - ## alternatives, e.g. `doAssert false` or raise a `Defect`. + ## alternatives, e.g. `raiseAssert` or raise a `Defect`. ## `quit` bypasses regular control flow in particular `defer`, ## `try`, `catch`, `finally` and `destructors`, and exceptions that may have been ## raised by an `addExitProc` proc, as well as cleanup code in other threads. diff --git a/testament/lib/stdtest/specialpaths.nim b/testament/lib/stdtest/specialpaths.nim index 7df63666f959..e214d113dfc6 100644 --- a/testament/lib/stdtest/specialpaths.nim +++ b/testament/lib/stdtest/specialpaths.nim @@ -48,7 +48,7 @@ proc splitTestFile*(file: string): tuple[cat: string, path: string] = else: result.path = file return result - doAssert false, "file must match this pattern: '/pathto/tests/dir/**/tfile.nim', got: '" & file & "'" + raiseAssert "file must match this pattern: '/pathto/tests/dir/**/tfile.nim', got: '" & file & "'" static: # sanity check From 23f3f9ae2ccb28cd5f9a6feaff92b9d26f4244e8 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 12 Aug 2023 08:30:17 +0800 Subject: [PATCH 167/347] better initialization patterns for seminst (#22456) * better initialization patterns for seminst * Update compiler/seminst.nim * Update compiler/seminst.nim --- compiler/seminst.nim | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index c7735903e030..61480494b283 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -29,11 +29,7 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) = else: discard proc pushProcCon*(c: PContext; owner: PSym) = - var x: PProcCon - new(x) - x.owner = owner - x.next = c.p - c.p = x + c.p = PProcCon(owner: owner, next: c.p) const errCannotInstantiateX = "cannot instantiate: '$1'" @@ -172,18 +168,13 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = internalAssert c.config, header.kind == tyGenericInvocation - var - cl: TReplTypeVars = default(TReplTypeVars) + var cl: TReplTypeVars = TReplTypeVars(symMap: initIdTable(), + localCache: initIdTable(), typeMap: LayeredIdTable(), + info: info, c: c, allowMetaTypes: allowMetaTypes + ) - cl.symMap = initIdTable() - cl.localCache = initIdTable() - cl.typeMap = LayeredIdTable() cl.typeMap.topLayer = initIdTable() - cl.info = info - cl.c = c - cl.allowMetaTypes = allowMetaTypes - # We must add all generic params in scope, because the generic body # may include tyFromExpr nodes depending on these generic params. # XXX: This looks quite similar to the code in matchUserTypeClass, From f642c9dbf112eb3a6fa993c8f1eee8a454c1c8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Sat, 12 Aug 2023 09:37:52 +0100 Subject: [PATCH 168/347] documents member (#22460) * documents member * Apply suggestions from code review Co-authored-by: Juan Carlos * Update doc/manual_experimental.md * Update doc/manual_experimental.md * Update doc/manual_experimental.md * Update doc/manual_experimental.md * Update doc/manual_experimental.md * Update doc/manual_experimental.md --------- Co-authored-by: Juan Carlos Co-authored-by: Andreas Rumpf --- doc/manual_experimental.md | 59 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 8cddce4f1437..b0ce775ee1dc 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2381,3 +2381,62 @@ proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized. + +Member pragma +============= + +Similar to the `constructor` and `virtual` pragmas, the `member` pragma can be used to attach a `proc` or `func` to a type in C++. +It is more flexible than `virtual` in the sense that it accepts not only names but also operators or destructors. + +For example: + +```nim +proc print(s: cstring) {.importcpp: "printf(@)", header: "".} + +type + Doo {.exportc.} = object + test: int + +proc memberProc(f: Doo) {.member.} = + echo $f.test + +proc destructor(f: Doo) {.member: "~'1()", used.} = + print "destructing\n" + +proc `==`(self, other: Doo): bool {.member: "operator==('2 const & #2) const -> '0".} = + self.test == other.test + +let doo = Doo(test: 2) +doo.memberProc() +echo doo == Doo(test: 1) + +``` + +Will print: +``` +2 +false +destructing +destructing +``` + +Notice how the C++ destructor is called automatically. Also notice the double implementation of `==` as an operator in Nim but also in C++. This is useful if you need the type to match some C++ `concept` or `trait` when interoping. + +A side effect of being able to declare C++ operators, is that you can now also create a +C++ functor to have seamless interop with C++ lambdas (syntactic sugar for functors). + +For example: + +```nim +type + NimFunctor = object + discard +proc invoke(f: NimFunctor; n: int) {.member: "operator ()('2 #2)".} = + echo "FunctorSupport!" + +{.experimental: "callOperator".} +proc `()`(f: NimFunctor; n:int) {.importcpp: "#(@)" .} +NimFunctor()(1) +``` +Notice we use the overload of `()` to have the same semantics in Nim, but on the `importcpp` we import the functor as a function. +This allows to easy interop with functions that accepts for example a `const` operator in its signature. \ No newline at end of file From 4c892231714fb64942b5014df0424de8fb732b73 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 12 Aug 2023 19:23:54 +0800 Subject: [PATCH 169/347] relax the parameter of `ensureMove`; allow let statements (#22466) * relax the parameter of `ensureMove`; allow let statements * fixes the test --- compiler/semmagic.nim | 5 +++-- tests/system/tensuremove.nim | 3 ++- tests/system/tensuremove2.nim | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index bee2a983ed19..d06b32e47a89 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -667,7 +667,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result.typ = expectedType # type inference for empty sequence # bug #21377 of mEnsureMove: result = n - if isAssignable(c, n[1]) notin {arLValue, arLocalLValue}: - localError(c.config, n.info, "'" & $n[1] & "'" & " is not a mutable location; it cannot be moved") + if n[1].kind in {nkStmtListExpr, nkBlockExpr, + nkIfExpr, nkCaseStmt, nkTryStmt}: + localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'") else: result = n diff --git a/tests/system/tensuremove.nim b/tests/system/tensuremove.nim index 980f2ea58242..52d9a43a8576 100644 --- a/tests/system/tensuremove.nim +++ b/tests/system/tensuremove.nim @@ -20,7 +20,8 @@ block: discard x.s proc main = - var x = X(s: "abcdefg") + let m = "abcdefg" + var x = X(s: ensureMove m) consume(ensureMove x) static: main() diff --git a/tests/system/tensuremove2.nim b/tests/system/tensuremove2.nim index 1fcbc1c0fa7b..39bbeb22e753 100644 --- a/tests/system/tensuremove2.nim +++ b/tests/system/tensuremove2.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'if true: s else: String()' is not a mutable location; it cannot be moved" + errormsg: "Nested expressions cannot be moved: 'if true: s else: String()'" """ type From 9207d77848d6f5db3635ae64f3cd4972cdbe3296 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 13 Aug 2023 06:02:36 +0800 Subject: [PATCH 170/347] fixes bareExcept warnings; catch specific exceptions (#21119) * fixes bareExcept warnings; catch specific exceptions * Update lib/pure/coro.nim --- lib/pure/asynchttpserver.nim | 2 +- lib/std/private/osdirs.nim | 2 +- lib/std/private/osfiles.nim | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 9b369c4bc7ae..0638d21aac99 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -381,7 +381,7 @@ const proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET) = ## Listen to the given port and address. when declared(maxDescriptors): - server.maxFDs = try: maxDescriptors() except: nimMaxDescriptorsFallback + server.maxFDs = try: maxDescriptors() except OSError: nimMaxDescriptorsFallback else: server.maxFDs = nimMaxDescriptorsFallback server.socket = newAsyncSocket(domain) diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index a4318367d843..e204b25e487a 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -515,7 +515,7 @@ proc copyDirWithPermissions*(source, dest: string, try: setFilePermissions(dest, getFilePermissions(source), followSymlinks = false) - except: + except OSError: if not ignorePermissionErrors: raise for kind, path in walkDir(source): diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index f2e7bf11d2fe..69a5af138119 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -311,7 +311,7 @@ proc copyFileWithPermissions*(source, dest: string, try: setFilePermissions(dest, getFilePermissions(source), followSymlinks = (cfSymlinkFollow in options)) - except: + except OSError: if not ignorePermissionErrors: raise @@ -402,6 +402,6 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", copyFile(source, dest, {cfSymlinkAsIs}) try: removeFile(source) - except: + except OSError: discard tryRemoveFile(dest) raise From 9bf605cf9801673eca96057935306ddd7fafbfe9 Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Mon, 14 Aug 2023 08:44:50 +0800 Subject: [PATCH 171/347] fixes syncio document (#22467) --- lib/std/syncio.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index 77d4fa04c8d1..a2a5c305b694 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -38,8 +38,8 @@ type ## at the end. If the file does not exist, it ## will be created. - FileHandle* = cint ## type that represents an OS file handle; this is - ## useful for low-level file access + FileHandle* = cint ## The type that represents an OS file handle; this is + ## useful for low-level file access. FileSeekPos* = enum ## Position relative to which seek should happen. # The values are ordered so that they match with stdio From 7bb2462d06b039b70e13b68ee2b23c39a881ca26 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:04:02 +0800 Subject: [PATCH 172/347] fixes CI (#22471) Revert "fixes bareExcept warnings; catch specific exceptions (#21119)" This reverts commit 9207d77848d6f5db3635ae64f3cd4972cdbe3296. --- lib/pure/asynchttpserver.nim | 2 +- lib/std/private/osdirs.nim | 2 +- lib/std/private/osfiles.nim | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 0638d21aac99..9b369c4bc7ae 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -381,7 +381,7 @@ const proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET) = ## Listen to the given port and address. when declared(maxDescriptors): - server.maxFDs = try: maxDescriptors() except OSError: nimMaxDescriptorsFallback + server.maxFDs = try: maxDescriptors() except: nimMaxDescriptorsFallback else: server.maxFDs = nimMaxDescriptorsFallback server.socket = newAsyncSocket(domain) diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index e204b25e487a..a4318367d843 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -515,7 +515,7 @@ proc copyDirWithPermissions*(source, dest: string, try: setFilePermissions(dest, getFilePermissions(source), followSymlinks = false) - except OSError: + except: if not ignorePermissionErrors: raise for kind, path in walkDir(source): diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index 69a5af138119..f2e7bf11d2fe 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -311,7 +311,7 @@ proc copyFileWithPermissions*(source, dest: string, try: setFilePermissions(dest, getFilePermissions(source), followSymlinks = (cfSymlinkFollow in options)) - except OSError: + except: if not ignorePermissionErrors: raise @@ -402,6 +402,6 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", copyFile(source, dest, {cfSymlinkAsIs}) try: removeFile(source) - except OSError: + except: discard tryRemoveFile(dest) raise From 09d0fda7fde69087c75a102b219d5eebf1b86db2 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 14 Aug 2023 19:08:01 +0800 Subject: [PATCH 173/347] fixes #22469; generates nimTestErrorFlag for top level statements (#22472) fixes #22469; generates `nimTestErrorFlag` for top level statements --- compiler/cgen.nim | 4 ++-- tests/exception/m22469.nim | 4 ++++ tests/exception/t22469.nim | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/exception/m22469.nim create mode 100644 tests/exception/t22469.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 5aafc506b2b6..811f89983425 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1875,13 +1875,13 @@ proc genInitCode(m: BModule) = if beforeRetNeeded in m.initProc.flags: prc.add("\tBeforeRet_: ;\n") - if sfMainModule in m.module.flags and m.config.exc == excGoto: + if m.config.exc == excGoto: if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: m.appcg(prc, "\t#nimTestErrorFlag();$n", []) if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: prc.add(deinitFrame(m.initProc)) - elif sfMainModule in m.module.flags and m.config.exc == excGoto: + elif m.config.exc == excGoto: if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: m.appcg(prc, "\t#nimTestErrorFlag();$n", []) diff --git a/tests/exception/m22469.nim b/tests/exception/m22469.nim new file mode 100644 index 000000000000..2016987015a1 --- /dev/null +++ b/tests/exception/m22469.nim @@ -0,0 +1,4 @@ +# ModuleB +echo "First top-level statement of ModuleB" +echo high(int) + 1 +echo "ModuleB last statement" \ No newline at end of file diff --git a/tests/exception/t22469.nim b/tests/exception/t22469.nim new file mode 100644 index 000000000000..a76c749678c2 --- /dev/null +++ b/tests/exception/t22469.nim @@ -0,0 +1,16 @@ +discard """ + exitcode: 1 + output: ''' +First top-level statement of ModuleB +m22469.nim(3) m22469 +fatal.nim(53) sysFatal +Error: unhandled exception: over- or underflow [OverflowDefect] +''' +""" + +# bug #22469 + +# ModuleA +import m22469 +echo "ModuleA about to have exception" +echo high(int) + 1 From 1927ae72d093d5e13bef6fd3fdf4700aa072f6bc Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Mon, 14 Aug 2023 20:00:48 +0100 Subject: [PATCH 174/347] Add Linux constant SO_BINDTODEVICE (#22468) --- lib/posix/posix_linux_amd64_consts.nim | 1 + lib/posix/posix_other_consts.nim | 1 + tools/detect/detect.nim | 1 + 3 files changed, 3 insertions(+) diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim index 6ac0279fcc99..fbe8d0666dbe 100644 --- a/lib/posix/posix_linux_amd64_consts.nim +++ b/lib/posix/posix_linux_amd64_consts.nim @@ -465,6 +465,7 @@ const MSG_EOR* = cint(128) const MSG_OOB* = cint(1) const SCM_RIGHTS* = cint(1) const SO_ACCEPTCONN* = cint(30) +const SO_BINDTODEVICE* = cint(25) const SO_BROADCAST* = cint(6) const SO_DEBUG* = cint(1) const SO_DONTROUTE* = cint(5) diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index f4809a9c276d..d346b4150d4c 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -482,6 +482,7 @@ var MSG_EOR* {.importc: "MSG_EOR", header: "".}: cint var MSG_OOB* {.importc: "MSG_OOB", header: "".}: cint var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "".}: cint var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "".}: cint +var SO_BINDTODEVICE* {.importc: "SO_BINDTODEVICE", header: "".}: cint var SO_BROADCAST* {.importc: "SO_BROADCAST", header: "".}: cint var SO_DEBUG* {.importc: "SO_DEBUG", header: "".}: cint var SO_DONTROUTE* {.importc: "SO_DONTROUTE", header: "".}: cint diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim index fe233420b520..ed9438494c8f 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -630,6 +630,7 @@ v("MSG_EOR") v("MSG_OOB") v("SCM_RIGHTS") v("SO_ACCEPTCONN") +v("SO_BINDTODEVICE") v("SO_BROADCAST") v("SO_DEBUG") v("SO_DONTROUTE") From a660c17d309e2b077c610fd8c8c697944cff676d Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 14 Aug 2023 22:27:36 -0600 Subject: [PATCH 175/347] Markdown code blocks migration part 8 (#22478) --- lib/pure/asynchttpserver.nim | 20 +-- lib/pure/collections/sets.nim | 20 ++- lib/pure/collections/sharedtables.nim | 4 +- lib/pure/collections/tables.nim | 33 ++-- lib/pure/colors.nim | 3 +- lib/pure/distros.nim | 5 +- lib/pure/htmlgen.nim | 3 +- lib/pure/htmlparser.nim | 10 +- lib/pure/httpclient.nim | 134 ++++++++------ lib/pure/json.nim | 18 +- lib/pure/logging.nim | 57 ++++-- lib/pure/memfiles.nim | 15 +- lib/pure/options.nim | 3 +- lib/pure/os.nim | 12 +- lib/pure/osproc.nim | 30 ++-- lib/pure/parsecfg.nim | 5 +- lib/pure/parsecsv.nim | 6 +- lib/pure/parseopt.nim | 25 +-- lib/pure/parseutils.nim | 39 ++-- lib/pure/parsexml.nim | 170 +++++++++--------- lib/pure/pegs.nim | 133 +++++++------- lib/pure/selectors.nim | 10 +- lib/pure/streams.nim | 88 ++++----- lib/pure/streamwrapper.nim | 4 +- lib/pure/strformat.nim | 9 +- lib/pure/strscans.nim | 32 ++-- lib/pure/strutils.nim | 114 +++++++----- lib/pure/times.nim | 12 +- lib/pure/unittest.nim | 86 +++++---- lib/pure/xmltree.nim | 3 +- lib/std/cmdline.nim | 9 +- lib/std/private/digitsutils.nim | 3 +- lib/std/private/since.nim | 8 +- lib/std/socketstreams.nim | 57 +++--- lib/std/typedthreads.nim | 32 ++-- lib/system.nim | 6 +- lib/system/channels_builtin.nim | 6 +- lib/system/dollars.nim | 27 +-- lib/system/gc.nim | 6 +- lib/system/nimscript.nim | 40 ++--- lib/system/repr_v2.nim | 19 +- lib/wrappers/openssl.nim | 3 +- nimpretty/tests/exhaustive.nim | 10 +- nimpretty/tests/expected/exhaustive.nim | 10 +- testament/specs.nim | 13 +- .../argument_parser/argument_parser.nim | 4 +- 46 files changed, 726 insertions(+), 630 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 9b369c4bc7ae..07eed9a5143b 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -110,16 +110,16 @@ proc respond*(req: Request, code: HttpCode, content: string, ## This procedure will **not** close the client socket. ## ## Example: - ## - ## .. code-block:: Nim - ## import std/json - ## proc handler(req: Request) {.async.} = - ## if req.url.path == "/hello-world": - ## let msg = %* {"message": "Hello World"} - ## let headers = newHttpHeaders([("Content-Type","application/json")]) - ## await req.respond(Http200, $msg, headers) - ## else: - ## await req.respond(Http404, "Not Found") + ## ```Nim + ## import std/json + ## proc handler(req: Request) {.async.} = + ## if req.url.path == "/hello-world": + ## let msg = %* {"message": "Hello World"} + ## let headers = newHttpHeaders([("Content-Type","application/json")]) + ## await req.respond(Http200, $msg, headers) + ## else: + ## await req.respond(Http404, "Not Found") + ## ``` var msg = "HTTP/1.1 " & $code & "\c\L" if headers != nil: diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 7e193af1a7ec..62abd68d4442 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -25,7 +25,9 @@ ## `difference <#difference,HashSet[A],HashSet[A]>`_, and ## `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_ ## -## .. code-block:: +## **Examples:** +## +## ```Nim ## echo toHashSet([9, 5, 1]) # {9, 1, 5} ## echo toOrderedSet([9, 5, 1]) # {9, 5, 1} ## @@ -37,7 +39,7 @@ ## echo s1 - s2 # {1, 9} ## echo s1 * s2 # {5} ## echo s1 -+- s2 # {9, 1, 3, 7} -## +## ``` ## ## Note: The data types declared here have *value semantics*: This means ## that `=` performs a copy of the set. @@ -249,7 +251,7 @@ iterator items*[A](s: HashSet[A]): A = ## If you need a sequence with the elements you can use `sequtils.toSeq ## template `_. ## - ## .. code-block:: + ## ```Nim ## type ## pair = tuple[a, b: int] ## var @@ -262,6 +264,7 @@ iterator items*[A](s: HashSet[A]): A = ## assert a.len == 2 ## echo b ## # --> {(a: 1, b: 3), (a: 0, b: 4)} + ## ``` let length = s.len for h in 0 .. high(s.data): if isFilled(s.data[h].hcode): @@ -586,12 +589,12 @@ proc `$`*[A](s: HashSet[A]): string = ## any moment and values are not escaped. ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## echo toHashSet([2, 4, 5]) ## # --> {2, 4, 5} ## echo toHashSet(["no", "esc'aping", "is \" provided"]) ## # --> {no, esc'aping, is " provided} + ## ``` dollarImpl() @@ -874,12 +877,12 @@ proc `$`*[A](s: OrderedSet[A]): string = ## any moment and values are not escaped. ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## echo toOrderedSet([2, 4, 5]) ## # --> {2, 4, 5} ## echo toOrderedSet(["no", "esc'aping", "is \" provided"]) ## # --> {no, esc'aping, is " provided} + ## ``` dollarImpl() @@ -890,7 +893,7 @@ iterator items*[A](s: OrderedSet[A]): A = ## If you need a sequence with the elements you can use `sequtils.toSeq ## template `_. ## - ## .. code-block:: + ## ```Nim ## var a = initOrderedSet[int]() ## for value in [9, 2, 1, 5, 1, 8, 4, 2]: ## a.incl(value) @@ -902,6 +905,7 @@ iterator items*[A](s: OrderedSet[A]): A = ## # --> Got 5 ## # --> Got 8 ## # --> Got 4 + ## ``` let length = s.len forAllOrderedPairs: yield s.data[h].key diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index 816ab49abba1..8b49066aca0d 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -191,8 +191,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, ## ## Example usage: ## - ## .. code-block:: nim - ## + ## ```nim ## # If value exists, decrement it. ## # If it becomes zero or less, delete the key ## t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool): @@ -200,6 +199,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, ## dec v ## if v <= 0: ## pairExists = false + ## ``` withLock t: var hc: Hash var index = rawGet(t, key, hc) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index d4056897db67..39dcddb5a9c0 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -136,14 +136,11 @@ runnableExamples: ## a more complex object as a key you will be greeted by a strange compiler ## error: ## -## .. code:: -## -## Error: type mismatch: got (Person) -## but expected one of: -## hashes.hash(x: openArray[A]): Hash -## hashes.hash(x: int): Hash -## hashes.hash(x: float): Hash -## … +## Error: type mismatch: got (Person) +## but expected one of: +## hashes.hash(x: openArray[A]): Hash +## hashes.hash(x: int): Hash +## hashes.hash(x: float): Hash ## ## What is happening here is that the types used for table keys require to have ## a `hash()` proc which will convert them to a `Hash `_ @@ -678,7 +675,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -692,6 +689,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) = ## # value: [2, 4, 6, 8] ## # key: o ## # value: [1, 5, 7, 9] + ## ``` let L = len(t) for h in 0 .. high(t.data): if isFilled(t.data[h].hcode): @@ -1127,7 +1125,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -1141,6 +1139,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = ## # value: [2, 4, 6, 8] ## # key: o ## # value: [1, 5, 7, 9] + ## ``` let L = len(t) for h in 0 .. high(t.data): if isFilled(t.data[h].hcode): @@ -1703,7 +1702,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -1717,6 +1716,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = ## # value: [1, 5, 7, 9] ## # key: e ## # value: [2, 4, 6, 8] + ## ``` let L = len(t) forAllOrderedPairs: @@ -2113,7 +2113,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -2127,6 +2127,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = ## # value: [1, 5, 7, 9] ## # key: e ## # value: [2, 4, 6, 8] + ## ``` let L = len(t) forAllOrderedPairs: @@ -2526,7 +2527,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = toCountTable("abracadabra") ## ## for k, v in pairs(a): @@ -2543,6 +2544,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) = ## # value: 1 ## # key: r ## # value: 2 + ## ``` let L = len(t) for h in 0 .. high(t.data): if t.data[h].val != 0: @@ -2806,7 +2808,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = newCountTable("abracadabra") ## ## for k, v in pairs(a): @@ -2823,6 +2825,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) = ## # value: 1 ## # key: r ## # value: 2 + ## ``` let L = len(t) for h in 0 .. high(t.data): if t.data[h].val != 0: @@ -2915,4 +2918,4 @@ proc hash*[K,V](s: OrderedTable[K,V]): Hash = proc hash*[V](s: CountTable[V]): Hash = for p in pairs(s): result = result xor hash(p) - result = !$result \ No newline at end of file + result = !$result diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index 685b68b360e4..eccccbfafc88 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -18,13 +18,14 @@ type proc `==`*(a, b: Color): bool {.borrow.} ## Compares two colors. ## - ## .. code-block:: + ## ```Nim ## var ## a = Color(0xff_00_ff) ## b = colFuchsia ## c = Color(0x00_ff_cc) ## assert a == b ## assert not (a == c) + ## ``` template extract(a: Color, r, g, b: untyped) = var r = a.int shr 16 and 0xff diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 25c961197b07..58eacf63346f 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -18,12 +18,11 @@ ## ## The above output could be the result of a code snippet like: ## -## .. code-block:: nim -## +## ```nim ## if detectOs(Ubuntu): ## foreignDep "lbiblas-dev" ## foreignDep "libvoodoo" -## +## ``` ## ## See `packaging `_ for hints on distributing Nim using OS packages. diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index bf31c0239779..be9e1fe90fb7 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -30,9 +30,10 @@ ## Examples ## ======== ## -## .. code-block:: Nim +## ```Nim ## var nim = "Nim" ## echo h1(a(href="https://nim-lang.org", nim)) +## ``` ## ## Writes the string: ## diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 24eab3abb951..0de384a8e645 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -12,10 +12,9 @@ ## ## It can be used to parse a wild HTML document and output it as valid XHTML ## document (well, if you are lucky): -## -## .. code-block:: Nim -## +## ```Nim ## echo loadHtml("mydirty.html") +## ``` ## ## Every tag in the resulting tree is in lower case. ## @@ -29,9 +28,7 @@ ## and write back the modified version. In this case we look for hyperlinks ## ending with the extension `.rst` and convert them to `.html`. ## -## .. code-block:: Nim -## :test: -## +## ```Nim test ## import std/htmlparser ## import std/xmltree # To use '$' for XmlNode ## import std/strtabs # To access XmlAttributes @@ -48,6 +45,7 @@ ## a.attrs["href"] = dir / filename & ".html" ## ## writeFile("output.html", $html) +## ``` import strutils, streams, parsexml, xmltree, unicode, strtabs diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index fd0ef3856476..ddf208e4e362 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -18,18 +18,19 @@ ## This example uses HTTP GET to retrieve ## `http://google.com`: ## -## .. code-block:: Nim +## ```Nim ## import std/httpclient ## var client = newHttpClient() ## try: ## echo client.getContent("http://google.com") ## finally: ## client.close() +## ``` ## ## The same action can also be performed asynchronously, simply use the ## `AsyncHttpClient`: ## -## .. code-block:: Nim +## ```Nim ## import std/[asyncdispatch, httpclient] ## ## proc asyncProc(): Future[string] {.async.} = @@ -40,6 +41,7 @@ ## client.close() ## ## echo waitFor asyncProc() +## ``` ## ## The functionality implemented by `HttpClient` and `AsyncHttpClient` ## is the same, so you can use whichever one suits you best in the examples @@ -59,7 +61,7 @@ ## uses `multipart/form-data` as the `Content-Type` to send the HTML to be ## validated to the server. ## -## .. code-block:: Nim +## ```Nim ## var client = newHttpClient() ## var data = newMultipartData() ## data["output"] = "soap12" @@ -69,13 +71,14 @@ ## echo client.postContent("http://validator.w3.org/check", multipart=data) ## finally: ## client.close() +## ``` ## ## To stream files from disk when performing the request, use `addFiles`. ## ## **Note:** This will allocate a new `Mimetypes` database every time you call ## it, you can pass your own via the `mimeDb` parameter to avoid this. ## -## .. code-block:: Nim +## ```Nim ## let mimes = newMimetypes() ## var client = newHttpClient() ## var data = newMultipartData() @@ -84,12 +87,13 @@ ## echo client.postContent("http://validator.w3.org/check", multipart=data) ## finally: ## client.close() +## ``` ## ## You can also make post requests with custom headers. ## This example sets `Content-Type` to `application/json` ## and uses a json object for the body ## -## .. code-block:: Nim +## ```Nim ## import std/[httpclient, json] ## ## let client = newHttpClient() @@ -102,6 +106,7 @@ ## echo response.status ## finally: ## client.close() +## ``` ## ## Progress reporting ## ================== @@ -110,27 +115,29 @@ ## This callback will be executed every second with information about the ## progress of the HTTP request. ## -## .. code-block:: Nim -## import std/[asyncdispatch, httpclient] +## ```Nim +## import std/[asyncdispatch, httpclient] ## -## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = -## echo("Downloaded ", progress, " of ", total) -## echo("Current rate: ", speed div 1000, "kb/s") +## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = +## echo("Downloaded ", progress, " of ", total) +## echo("Current rate: ", speed div 1000, "kb/s") ## -## proc asyncProc() {.async.} = -## var client = newAsyncHttpClient() -## client.onProgressChanged = onProgressChanged -## try: -## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") -## finally: -## client.close() +## proc asyncProc() {.async.} = +## var client = newAsyncHttpClient() +## client.onProgressChanged = onProgressChanged +## try: +## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") +## finally: +## client.close() ## -## waitFor asyncProc() +## waitFor asyncProc() +## ``` ## ## If you would like to remove the callback simply set it to `nil`. ## -## .. code-block:: Nim +## ```Nim ## client.onProgressChanged = nil +## ``` ## ## .. warning:: The `total` reported by httpclient may be 0 in some cases. ## @@ -152,9 +159,10 @@ ## ## Example of setting SSL verification parameters in a new client: ## -## .. code-block:: Nim -## import httpclient -## var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer)) +## ```Nim +## import httpclient +## var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer)) +## ``` ## ## There are three options for verify mode: ## @@ -183,10 +191,11 @@ ## ## Here is how to set a timeout when creating an `HttpClient` instance: ## -## .. code-block:: Nim -## import std/httpclient +## ```Nim +## import std/httpclient ## -## let client = newHttpClient(timeout = 42) +## let client = newHttpClient(timeout = 42) +## ``` ## ## Proxy ## ===== @@ -197,36 +206,39 @@ ## ## Some examples on how to configure a Proxy for `HttpClient`: ## -## .. code-block:: Nim -## import std/httpclient +## ```Nim +## import std/httpclient ## -## let myProxy = newProxy("http://myproxy.network") -## let client = newHttpClient(proxy = myProxy) +## let myProxy = newProxy("http://myproxy.network") +## let client = newHttpClient(proxy = myProxy) +## ``` ## ## Use proxies with basic authentication: ## -## .. code-block:: Nim -## import std/httpclient -## -## let myProxy = newProxy("http://myproxy.network", auth="user:password") -## let client = newHttpClient(proxy = myProxy) +## ```Nim +## import std/httpclient +## +## let myProxy = newProxy("http://myproxy.network", auth="user:password") +## let client = newHttpClient(proxy = myProxy) +## ``` ## ## Get Proxy URL from environment variables: ## -## .. code-block:: Nim -## import std/httpclient +## ```Nim +## import std/httpclient ## -## var url = "" -## try: -## if existsEnv("http_proxy"): -## url = getEnv("http_proxy") -## elif existsEnv("https_proxy"): -## url = getEnv("https_proxy") -## except ValueError: -## echo "Unable to parse proxy from environment variables." +## var url = "" +## try: +## if existsEnv("http_proxy"): +## url = getEnv("http_proxy") +## elif existsEnv("https_proxy"): +## url = getEnv("https_proxy") +## except ValueError: +## echo "Unable to parse proxy from environment variables." ## -## let myProxy = newProxy(url = url) -## let client = newHttpClient(proxy = myProxy) +## let myProxy = newProxy(url = url) +## let client = newHttpClient(proxy = myProxy) +## ``` ## ## Redirects ## ========= @@ -237,10 +249,11 @@ ## ## Here you can see an example about how to set the `maxRedirects` of `HttpClient`: ## -## .. code-block:: Nim -## import std/httpclient +## ```Nim +## import std/httpclient ## -## let client = newHttpClient(maxRedirects = 0) +## let client = newHttpClient(maxRedirects = 0) +## ``` ## import std/private/since @@ -429,8 +442,9 @@ proc add*(p: MultipartData, xs: MultipartEntries): MultipartData ## Add a list of multipart entries to the multipart data `p`. All values are ## added without a filename and without a content type. ## - ## .. code-block:: Nim + ## ```Nim ## data.add({"action": "login", "format": "json"}) + ## ``` for name, content in xs.items: p.add(name, content) result = p @@ -439,8 +453,9 @@ proc newMultipartData*(xs: MultipartEntries): MultipartData = ## Create a new multipart data object and fill it with the entries `xs` ## directly. ## - ## .. code-block:: Nim + ## ```Nim ## var data = newMultipartData({"action": "login", "format": "json"}) + ## ``` result = MultipartData() for entry in xs: result.add(entry.name, entry.content) @@ -455,8 +470,9 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]], ## Raises an `IOError` if the file cannot be opened or reading fails. To ## manually specify file content, filename and MIME type, use `[]=` instead. ## - ## .. code-block:: Nim + ## ```Nim ## data.addFiles({"uploaded_file": "public/test.html"}) + ## ``` for name, file in xs.items: var contentType: string let (_, fName, ext) = splitFile(file) @@ -470,8 +486,9 @@ proc `[]=`*(p: MultipartData, name, content: string) {.inline.} = ## Add a multipart entry to the multipart data `p`. The value is added ## without a filename and without a content type. ## - ## .. code-block:: Nim + ## ```Nim ## data["username"] = "NimUser" + ## ``` p.add(name, content) proc `[]=`*(p: MultipartData, name: string, @@ -479,9 +496,10 @@ proc `[]=`*(p: MultipartData, name: string, ## Add a file to the multipart data `p`, specifying filename, contentType ## and content manually. ## - ## .. code-block:: Nim + ## ```Nim ## data["uploaded_file"] = ("test.html", "text/html", ## "

    test

    ") + ## ``` p.add(name, file.content, file.name, file.contentType, useStream = false) proc getBoundary(p: MultipartData): string = @@ -688,15 +706,15 @@ proc close*(client: HttpClient | AsyncHttpClient) = client.connected = false proc getSocket*(client: HttpClient): Socket {.inline.} = - ## Get network socket, useful if you want to find out more details about the connection + ## Get network socket, useful if you want to find out more details about the connection. ## - ## this example shows info about local and remote endpoints + ## This example shows info about local and remote endpoints: ## - ## .. code-block:: Nim + ## ```Nim ## if client.connected: ## echo client.getSocket.getLocalAddr ## echo client.getSocket.getPeerAddr - ## + ## ``` return client.socket proc getSocket*(client: AsyncHttpClient): AsyncSocket {.inline.} = diff --git a/lib/pure/json.nim b/lib/pure/json.nim index fcb9eae41b6b..8c1f19fd3427 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -41,13 +41,14 @@ ## For a `JsonNode` who's kind is `JObject`, you can access its fields using ## the `[]` operator. The following example shows how to do this: ## -## .. code-block:: Nim +## ```Nim ## import std/json ## ## let jsonNode = parseJson("""{"key": 3.14}""") ## ## doAssert jsonNode.kind == JObject ## doAssert jsonNode["key"].kind == JFloat +## ``` ## ## Reading values ## -------------- @@ -62,12 +63,13 @@ ## ## To retrieve the value of `"key"` you can do the following: ## -## .. code-block:: Nim +## ```Nim ## import std/json ## ## let jsonNode = parseJson("""{"key": 3.14}""") ## ## doAssert jsonNode["key"].getFloat() == 3.14 +## ``` ## ## **Important:** The `[]` operator will raise an exception when the ## specified field does not exist. @@ -79,7 +81,7 @@ ## when the field is not found. The `get`-family of procedures will return a ## type's default value when called on `nil`. ## -## .. code-block:: Nim +## ```Nim ## import std/json ## ## let jsonNode = parseJson("{}") @@ -88,6 +90,7 @@ ## doAssert jsonNode{"nope"}.getFloat() == 0 ## doAssert jsonNode{"nope"}.getStr() == "" ## doAssert jsonNode{"nope"}.getBool() == false +## ``` ## ## Using default values ## -------------------- @@ -95,7 +98,7 @@ ## The `get`-family helpers also accept an additional parameter which allow ## you to fallback to a default value should the key's values be `null`: ## -## .. code-block:: Nim +## ```Nim ## import std/json ## ## let jsonNode = parseJson("""{"key": 3.14, "key2": null}""") @@ -103,6 +106,7 @@ ## doAssert jsonNode["key"].getFloat(6.28) == 3.14 ## doAssert jsonNode["key2"].getFloat(3.14) == 3.14 ## doAssert jsonNode{"nope"}.getFloat(3.14) == 3.14 # note the {} +## ``` ## ## Unmarshalling ## ------------- @@ -113,7 +117,7 @@ ## Note: Use `Option `_ for keys sometimes missing in json ## responses, and backticks around keys with a reserved keyword as name. ## -## .. code-block:: Nim +## ```Nim ## import std/json ## import std/options ## @@ -127,6 +131,7 @@ ## let user = to(userJson, User) ## if user.`type`.isSome(): ## assert user.`type`.get() != "robot" +## ``` ## ## Creating JSON ## ============= @@ -134,7 +139,7 @@ ## This module can also be used to comfortably create JSON using the `%*` ## operator: ## -## .. code-block:: nim +## ```nim ## import std/json ## ## var hisName = "John" @@ -148,6 +153,7 @@ ## var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]} ## j2["details"] = %* {"age":35, "pi":3.1415} ## echo j2 +## ``` ## ## See also: std/jsonutils for hookable json serialization/deserialization ## of arbitrary types. diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 46b59b750fe4..1767ee3f682e 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -17,10 +17,11 @@ ## ## To get started, first create a logger: ## -## .. code-block:: +## ```Nim ## import std/logging ## ## var logger = newConsoleLogger() +## ``` ## ## The logger that was created above logs to the console, but this module ## also provides loggers that log to files, such as the @@ -30,9 +31,10 @@ ## Once a logger has been created, call its `log proc ## <#log.e,ConsoleLogger,Level,varargs[string,]>`_ to log a message: ## -## .. code-block:: +## ```Nim ## logger.log(lvlInfo, "a log message") ## # Output: INFO a log message +## ``` ## ## The ``INFO`` within the output is the result of a format string being ## prepended to the message, and it will differ depending on the message's @@ -58,7 +60,7 @@ ## used with the `addHandler proc<#addHandler,Logger>`_, which is demonstrated ## in the following example: ## -## .. code-block:: +## ```Nim ## import std/logging ## ## var consoleLog = newConsoleLogger() @@ -68,17 +70,19 @@ ## addHandler(consoleLog) ## addHandler(fileLog) ## addHandler(rollingLog) +## ``` ## ## After doing this, use either the `log template ## <#log.t,Level,varargs[string,]>`_ or one of the level-specific templates, ## such as the `error template<#error.t,varargs[string,]>`_, to log messages ## to all registered handlers at once. ## -## .. code-block:: +## ```Nim ## # This example uses the loggers created above ## log(lvlError, "an error occurred") ## error("an error occurred") # Equivalent to the above line ## info("something normal happened") # Will not be written to errors.log +## ``` ## ## Note that a message's level is still checked against each handler's ## ``levelThreshold`` and the global log filter. @@ -116,12 +120,13 @@ ## ## The following example illustrates how to use format strings: ## -## .. code-block:: +## ```Nim ## import std/logging ## ## var logger = newConsoleLogger(fmtStr="[$time] - $levelname: ") ## logger.log(lvlInfo, "this is a message") ## # Output: [19:50:13] - INFO: this is a message +## ``` ## ## Notes when using multiple threads ## --------------------------------- @@ -372,10 +377,11 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var consoleLog = newConsoleLogger() ## consoleLog.log(lvlInfo, "this is a message") ## consoleLog.log(lvlError, "error code is: ", 404) + ## ``` if level >= logging.level and level >= logger.levelThreshold: let ln = substituteLog(logger.fmtStr, level, args) when defined(js): @@ -414,10 +420,11 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr, ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var normalLog = newConsoleLogger() ## var formatLog = newConsoleLogger(fmtStr=verboseFmtStr) ## var errorLog = newConsoleLogger(levelThreshold=lvlError, useStderr=true) + ## ``` new result result.fmtStr = fmtStr result.levelThreshold = levelThreshold @@ -450,10 +457,11 @@ when not defined(js): ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var fileLog = newFileLogger("messages.log") ## fileLog.log(lvlInfo, "this is a message") ## fileLog.log(lvlError, "error code is: ", 404) + ## ``` if level >= logging.level and level >= logger.levelThreshold: writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) if level >= logger.flushThreshold: flushFile(logger.file) @@ -481,7 +489,7 @@ when not defined(js): ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var messages = open("messages.log", fmWrite) ## var formatted = open("formatted.log", fmWrite) ## var errors = open("errors.log", fmWrite) @@ -489,6 +497,7 @@ when not defined(js): ## var normalLog = newFileLogger(messages) ## var formatLog = newFileLogger(formatted, fmtStr=verboseFmtStr) ## var errorLog = newFileLogger(errors, levelThreshold=lvlError) + ## ``` new(result) result.file = file result.levelThreshold = levelThreshold @@ -519,10 +528,11 @@ when not defined(js): ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var normalLog = newFileLogger("messages.log") ## var formatLog = newFileLogger("formatted.log", fmtStr=verboseFmtStr) ## var errorLog = newFileLogger("errors.log", levelThreshold=lvlError) + ## ``` let file = open(filename, mode, bufSize = bufSize) newFileLogger(file, levelThreshold, fmtStr, flushThreshold) @@ -579,11 +589,12 @@ when not defined(js): ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var normalLog = newRollingFileLogger("messages.log") ## var formatLog = newRollingFileLogger("formatted.log", fmtStr=verboseFmtStr) ## var shortLog = newRollingFileLogger("short.log", maxLines=200) ## var errorLog = newRollingFileLogger("errors.log", levelThreshold=lvlError) + ## ``` new(result) result.levelThreshold = levelThreshold result.fmtStr = fmtStr @@ -633,10 +644,11 @@ when not defined(js): ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var rollingLog = newRollingFileLogger("messages.log") ## rollingLog.log(lvlInfo, "this is a message") ## rollingLog.log(lvlError, "error code is: ", 404) + ## ``` if level >= logging.level and level >= logger.levelThreshold: if logger.curLine >= logger.maxLines: logger.file.close() @@ -666,11 +678,12 @@ template log*(level: Level, args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## log(lvlInfo, "This is an example.") + ## ``` ## ## See also: ## * `debug template<#debug.t,varargs[string,]>`_ @@ -695,11 +708,12 @@ template debug*(args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## debug("myProc called with arguments: foo, 5") + ## ``` ## ## See also: ## * `log template<#log.t,Level,varargs[string,]>`_ @@ -716,11 +730,12 @@ template info*(args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## info("Application started successfully.") + ## ``` ## ## See also: ## * `log template<#log.t,Level,varargs[string,]>`_ @@ -737,11 +752,12 @@ template notice*(args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## notice("An important operation has completed.") + ## ``` ## ## See also: ## * `log template<#log.t,Level,varargs[string,]>`_ @@ -757,11 +773,12 @@ template warn*(args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## warn("The previous operation took too long to process.") + ## ``` ## ## See also: ## * `log template<#log.t,Level,varargs[string,]>`_ @@ -779,11 +796,12 @@ template error*(args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## error("An exception occurred while processing the form.") + ## ``` ## ## See also: ## * `log template<#log.t,Level,varargs[string,]>`_ @@ -800,11 +818,12 @@ template fatal*(args: varargs[string, `$`]) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## var logger = newConsoleLogger() ## addHandler(logger) ## ## fatal("Can't open database -- exiting.") + ## ``` ## ## See also: ## * `log template<#log.t,Level,varargs[string,]>`_ diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 74065bc2e8bc..e27f9a79c3b3 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -150,7 +150,7 @@ proc open*(filename: string, mode: FileMode = fmRead, ## ## Example: ## - ## .. code-block:: nim + ## ```nim ## var ## mm, mm_full, mm_half: MemFile ## @@ -162,6 +162,7 @@ proc open*(filename: string, mode: FileMode = fmRead, ## ## # Read the first 512 bytes ## mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512) + ## ``` # The file can be resized only when write mode is used: if mode == fmAppend: @@ -443,13 +444,13 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline ## functions, not str* functions). ## ## Example: - ## - ## .. code-block:: nim + ## ```nim ## var count = 0 ## for slice in memSlices(memfiles.open("foo")): ## if slice.size > 0 and cast[cstring](slice.data)[0] != '#': ## inc(count) ## echo count + ## ``` proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. importc: "memchr", header: "".} @@ -479,11 +480,11 @@ iterator lines*(mfile: MemFile, buf: var string, delim = '\l', ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned. ## ## Example: - ## - ## .. code-block:: nim + ## ```nim ## var buffer: string = "" ## for line in lines(memfiles.open("foo"), buffer): ## echo line + ## ``` for ms in memSlices(mfile, delim, eat): setLen(buf, ms.size) @@ -498,10 +499,10 @@ iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} = ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned. ## ## Example: - ## - ## .. code-block:: nim + ## ```nim ## for line in lines(memfiles.open("foo")): ## echo line + ## ``` var buf = newStringOfCap(80) for line in lines(mfile, buf, delim, eat): diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 9dc4e096b33b..ec384ceb4ba3 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -53,7 +53,7 @@ Pattern matching supports pattern matching on `Option`s, with the `Some()` and `None()` patterns. -.. code-block:: nim + ```nim {.experimental: "caseStmtMacros".} import fusion/matching @@ -65,6 +65,7 @@ supports pattern matching on `Option`s, with the `Some()` and assert false assertMatch(some(some(none(int))), Some(Some(None()))) + ``` ]## # xxx pending https://github.com/timotheecour/Nim/issues/376 use `runnableExamples` and `whichModule` diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 13c6d6113ad0..77dc3ca8f865 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -411,9 +411,9 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", ## `_. ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## discard execShellCmd("ls -la") + ## ``` result = exitStatusLikeShell(c_system(command)) proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", @@ -482,18 +482,18 @@ proc inclFilePermissions*(filename: string, permissions: set[FilePermission]) {. rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} = ## A convenience proc for: - ## - ## .. code-block:: nim + ## ```nim ## setFilePermissions(filename, getFilePermissions(filename)+permissions) + ## ``` setFilePermissions(filename, getFilePermissions(filename)+permissions) proc exclFilePermissions*(filename: string, permissions: set[FilePermission]) {. rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} = ## A convenience proc for: - ## - ## .. code-block:: nim + ## ```nim ## setFilePermissions(filename, getFilePermissions(filename)-permissions) + ## ``` setFilePermissions(filename, getFilePermissions(filename)-permissions) when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netbsd)): diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 91c45e0539f3..b8dd153f2402 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -91,12 +91,12 @@ proc execProcess*(command: string, workingDir: string = "", ## * `execCmd proc <#execCmd,string>`_ ## ## Example: - ## - ## .. code-block:: Nim - ## let outp = execProcess("nim", args=["c", "-r", "mytestfile.nim"], options={poUsePath}) - ## let outp_shell = execProcess("nim c -r mytestfile.nim") - ## # Note: outp may have an interleave of text from the nim compile - ## # and any output from mytestfile when it runs + ## ```Nim + ## let outp = execProcess("nim", args=["c", "-r", "mytestfile.nim"], options={poUsePath}) + ## let outp_shell = execProcess("nim c -r mytestfile.nim") + ## # Note: outp may have an interleave of text from the nim compile + ## # and any output from mytestfile when it runs + ## ``` proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [ExecIOEffect, ReadIOEffect, RootEffect].} @@ -113,9 +113,9 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## ## Example: - ## - ## .. code-block:: Nim - ## let errC = execCmd("nim c -r mytestfile.nim") + ## ```Nim + ## let errC = execCmd("nim c -r mytestfile.nim") + ## ``` proc startProcess*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, @@ -465,8 +465,7 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raise ## * `readLines proc <#readLines,Process>`_ ## ## Example: - ## - ## .. code-block:: Nim + ## ```Nim ## const opts = {poUsePath, poDaemon, poStdErrToStdOut} ## var ps: seq[Process] ## for prog in ["a", "b"]: # run 2 progs in parallel @@ -478,6 +477,7 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raise ## i.inc ## if i > 100: break ## p.close + ## ``` var outp = p.outputStream var line = newStringOfCap(120) while outp.readLine(line): @@ -495,8 +495,7 @@ proc readLines*(p: Process): (seq[string], int) {.since: (1, 3), ## * `lines iterator <#lines.i,Process>`_ ## ## Example: - ## - ## .. code-block:: Nim + ## ```Nim ## const opts = {poUsePath, poDaemon, poStdErrToStdOut} ## var ps: seq[Process] ## for prog in ["a", "b"]: # run 2 progs in parallel @@ -506,6 +505,7 @@ proc readLines*(p: Process): (seq[string], int) {.since: (1, 3), ## if exCode != 0: ## for line in lines: echo line ## p.close + ## ``` for line in p.lines: result[0].add(line) result[1] = p.peekExitCode @@ -1587,8 +1587,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## ## Example: - ## - ## .. code-block:: Nim + ## ```Nim ## var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") ## import std/[strutils, strtabs] ## stripLineEnd(result[0]) ## portable way to remove trailing newline, if any @@ -1597,6 +1596,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## when defined(posix): ## assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) ## assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) + ## ``` when (NimMajor, NimMinor, NimPatch) < (1, 3, 5): doAssert input.len == 0 diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 54584a253654..3ba62ebd144b 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -45,9 +45,7 @@ runnableExamples("-r:off"): ## Configuration file example ]## -## -## .. code-block:: nim -## +## ```none ## charset = "utf-8" ## [Package] ## name = "hello" @@ -55,6 +53,7 @@ runnableExamples("-r:off"): ## [Author] ## name = "nim-lang" ## website = "nim-lang.org" +## ``` ##[ ## Creating a configuration file diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index a8d1cfaabc34..dcd486c089bd 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -13,7 +13,7 @@ ## Basic usage ## =========== ## -## .. code-block:: nim +## ```nim ## import std/parsecsv ## from std/os import paramStr ## from std/streams import newFileStream @@ -29,11 +29,12 @@ ## for val in items(x.row): ## echo "##", val, "##" ## close(x) +## ``` ## ## For CSV files with a header row, the header can be read and then used as a ## reference for item access with `rowEntry <#rowEntry,CsvParser,string>`_: ## -## .. code-block:: nim +## ```nim ## import std/parsecsv ## ## # Prepare a file @@ -52,6 +53,7 @@ ## for col in items(p.headers): ## echo "##", col, ":", p.rowEntry(col), "##" ## p.close() +## ``` ## ## See also ## ======== diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 2d039f1e4ad1..6674a7272a5c 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -48,7 +48,7 @@ ## ## Here is an example: ## -## .. code-block:: +## ```Nim ## import std/parseopt ## ## var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt") @@ -71,6 +71,7 @@ ## # Option: foo ## # Option and value: bar, 20 ## # Argument: file.txt +## ``` ## ## The `getopt iterator<#getopt.i,OptParser>`_, which is provided for ## convenience, can be used to iterate through all command line options as well. @@ -80,7 +81,8 @@ ## Then set the variable to the new value while parsing. ## ## Here is an example: -## .. code-block:: +## +## ```Nim ## import std/parseopt ## ## var varName: string = "defaultValue" @@ -95,6 +97,7 @@ ## varName = val # do input sanitization in production systems ## of cmdEnd: ## discard +## ``` ## ## `shortNoVal` and `longNoVal` ## ============================ @@ -119,7 +122,7 @@ ## `shortNoVal` and `longNoVal`, which is the default, and providing ## arguments for those two parameters: ## -## .. code-block:: +## ```Nim ## import std/parseopt ## ## proc printToken(kind: CmdLineKind, key: string, val: string) = @@ -153,6 +156,7 @@ ## # Output: ## # Option and value: j, 4 ## # Option and value: first, bar +## ``` ## ## See also ## ======== @@ -387,14 +391,14 @@ when declared(quoteShellCommand): ## * `remainingArgs proc<#remainingArgs,OptParser>`_ ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## var p = initOptParser("--left -r:2 -- foo.txt bar.txt") ## while true: ## p.next() ## if p.kind == cmdLongOption and p.key == "": # Look for "--" ## break ## doAssert p.cmdLineRest == "foo.txt bar.txt" + ## ``` result = p.cmds[p.idx .. ^1].quoteShellCommand proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} = @@ -404,14 +408,14 @@ proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} = ## * `cmdLineRest proc<#cmdLineRest,OptParser>`_ ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## var p = initOptParser("--left -r:2 -- foo.txt bar.txt") ## while true: ## p.next() ## if p.kind == cmdLongOption and p.key == "": # Look for "--" ## break ## doAssert p.remainingArgs == @["foo.txt", "bar.txt"] + ## ``` result = @[] for i in p.idx..`_ module. ## -## .. code-block:: nim -## :test: +## ```nim test +## let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"] +## var outp: seq[string] ## -## let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"] -## var outp: seq[string] +## for log in logs: +## var res: string +## if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10 +## outp.add(res & " - " & captureBetween(log, ' ', '_')) +## doAssert outp == @["2019-01-10 - OK", "2019-01-11 - FAIL"] +## ``` ## -## for log in logs: -## var res: string -## if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10 -## outp.add(res & " - " & captureBetween(log, ' ', '_')) -## doAssert outp == @["2019-01-10 - OK", "2019-01-11 - FAIL"] +## ```nim test +## from std/strutils import Digits, parseInt ## -## .. code-block:: nim -## :test: -## from std/strutils import Digits, parseInt -## -## let -## input1 = "2019 school start" -## input2 = "3 years back" -## startYear = input1[0 .. skipWhile(input1, Digits)-1] # 2019 -## yearsBack = input2[0 .. skipWhile(input2, Digits)-1] # 3 -## examYear = parseInt(startYear) + parseInt(yearsBack) -## doAssert "Examination is in " & $examYear == "Examination is in 2022" +## let +## input1 = "2019 school start" +## input2 = "3 years back" +## startYear = input1[0 .. skipWhile(input1, Digits)-1] # 2019 +## yearsBack = input2[0 .. skipWhile(input2, Digits)-1] # 3 +## examYear = parseInt(startYear) + parseInt(yearsBack) +## doAssert "Examination is in " & $examYear == "Examination is in 2022" +## ``` ## ## **See also:** ## * `strutils module`_ for combined and identical parsing proc's diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 884f258f3866..88cb6d9c063d 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -36,43 +36,43 @@ The file ``examples/htmltitle.nim`` demonstrates how to use the XML parser to accomplish a simple task: To determine the title of an HTML document. -.. code-block:: nim + ```nim + # Example program to show the parsexml module + # This program reads an HTML file and writes its title to stdout. + # Errors and whitespace are ignored. - # Example program to show the parsexml module - # This program reads an HTML file and writes its title to stdout. - # Errors and whitespace are ignored. + import os, streams, parsexml, strutils - import os, streams, parsexml, strutils + if paramCount() < 1: + quit("Usage: htmltitle filename[.html]") - if paramCount() < 1: - quit("Usage: htmltitle filename[.html]") - - var filename = addFileExt(paramStr(1), "html") - var s = newFileStream(filename, fmRead) - if s == nil: quit("cannot open the file " & filename) - var x: XmlParser - open(x, s, filename) - while true: - x.next() - case x.kind - of xmlElementStart: - if cmpIgnoreCase(x.elementName, "title") == 0: - var title = "" - x.next() # skip "" - while x.kind == xmlCharData: - title.add(x.charData) - x.next() - if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0: - echo("Title: " & title) - quit(0) # Success! - else: - echo(x.errorMsgExpected("/title")) + var filename = addFileExt(paramStr(1), "html") + var s = newFileStream(filename, fmRead) + if s == nil: quit("cannot open the file " & filename) + var x: XmlParser + open(x, s, filename) + while true: + x.next() + case x.kind + of xmlElementStart: + if cmpIgnoreCase(x.elementName, "title") == 0: + var title = "" + x.next() # skip "<title>" + while x.kind == xmlCharData: + title.add(x.charData) + x.next() + if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0: + echo("Title: " & title) + quit(0) # Success! + else: + echo(x.errorMsgExpected("/title")) - of xmlEof: break # end of file reached - else: discard # ignore other events + of xmlEof: break # end of file reached + else: discard # ignore other events - x.close() - quit("Could not determine title!") + x.close() + quit("Could not determine title!") + ``` ]## @@ -85,64 +85,64 @@ The file ``examples/htmlrefs.nim`` demonstrates how to use the XML parser to accomplish another simple task: To determine all the links an HTML document contains. -.. code-block:: nim + ```nim + # Example program to show the new parsexml module + # This program reads an HTML file and writes all its used links to stdout. + # Errors and whitespace are ignored. - # Example program to show the new parsexml module - # This program reads an HTML file and writes all its used links to stdout. - # Errors and whitespace are ignored. + import os, streams, parsexml, strutils - import os, streams, parsexml, strutils + proc `=?=` (a, b: string): bool = + # little trick: define our own comparator that ignores case + return cmpIgnoreCase(a, b) == 0 - proc `=?=` (a, b: string): bool = - # little trick: define our own comparator that ignores case - return cmpIgnoreCase(a, b) == 0 + if paramCount() < 1: + quit("Usage: htmlrefs filename[.html]") - if paramCount() < 1: - quit("Usage: htmlrefs filename[.html]") - - var links = 0 # count the number of links - var filename = addFileExt(paramStr(1), "html") - var s = newFileStream(filename, fmRead) - if s == nil: quit("cannot open the file " & filename) - var x: XmlParser - open(x, s, filename) - next(x) # get first event - block mainLoop: - while true: - case x.kind - of xmlElementOpen: - # the <a href = "xyz"> tag we are interested in always has an attribute, - # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart`` - if x.elementName =?= "a": - x.next() - if x.kind == xmlAttribute: - if x.attrKey =?= "href": - var link = x.attrValue - inc(links) - # skip until we have an ``xmlElementClose`` event - while true: - x.next() - case x.kind - of xmlEof: break mainLoop - of xmlElementClose: break - else: discard - x.next() # skip ``xmlElementClose`` - # now we have the description for the ``a`` element - var desc = "" - while x.kind == xmlCharData: - desc.add(x.charData) - x.next() - echo(desc & ": " & link) - else: - x.next() - of xmlEof: break # end of file reached - of xmlError: - echo(errorMsg(x)) + var links = 0 # count the number of links + var filename = addFileExt(paramStr(1), "html") + var s = newFileStream(filename, fmRead) + if s == nil: quit("cannot open the file " & filename) + var x: XmlParser + open(x, s, filename) + next(x) # get first event + block mainLoop: + while true: + case x.kind + of xmlElementOpen: + # the <a href = "xyz"> tag we are interested in always has an attribute, + # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart`` + if x.elementName =?= "a": x.next() - else: x.next() # skip other events - - echo($links & " link(s) found!") - x.close() + if x.kind == xmlAttribute: + if x.attrKey =?= "href": + var link = x.attrValue + inc(links) + # skip until we have an ``xmlElementClose`` event + while true: + x.next() + case x.kind + of xmlEof: break mainLoop + of xmlElementClose: break + else: discard + x.next() # skip ``xmlElementClose`` + # now we have the description for the ``a`` element + var desc = "" + while x.kind == xmlCharData: + desc.add(x.charData) + x.next() + echo(desc & ": " & link) + else: + x.next() + of xmlEof: break # end of file reached + of xmlError: + echo(errorMsg(x)) + x.next() + else: x.next() # skip other events + + echo($links & " link(s) found!") + x.close() + ``` ]## diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 11683bbff9ee..7f0f532fe5e5 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -889,7 +889,7 @@ macro mkHandlerTplts(handlers: untyped): untyped = # Transforms the handler spec in *handlers* into handler templates. # The AST structure of *handlers[0]*: # - # .. code-block:: + # ``` # StmtList # Call # Ident "pkNonTerminal" @@ -910,6 +910,7 @@ macro mkHandlerTplts(handlers: untyped): untyped = # StmtList # <handler code block> # ... + # ``` func mkEnter(hdName, body: NimNode): NimNode = template helper(hdName, body) {.dirty.} = template hdName(s, p, start) = @@ -959,60 +960,61 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = ## match, else the length of the total match. The following example code ## evaluates an arithmetic expression defined by a simple PEG: ## - ## .. code-block:: nim - ## import std/[strutils, pegs] + ## ```nim + ## import std/[strutils, pegs] ## - ## let - ## pegAst = """ - ## Expr <- Sum - ## Sum <- Product (('+' / '-')Product)* - ## Product <- Value (('*' / '/')Value)* - ## Value <- [0-9]+ / '(' Expr ')' - ## """.peg - ## txt = "(5+3)/2-7*22" + ## let + ## pegAst = """ + ## Expr <- Sum + ## Sum <- Product (('+' / '-')Product)* + ## Product <- Value (('*' / '/')Value)* + ## Value <- [0-9]+ / '(' Expr ')' + ## """.peg + ## txt = "(5+3)/2-7*22" ## - ## var - ## pStack: seq[string] = @[] - ## valStack: seq[float] = @[] - ## opStack = "" - ## let - ## parseArithExpr = pegAst.eventParser: - ## pkNonTerminal: - ## enter: - ## pStack.add p.nt.name - ## leave: - ## pStack.setLen pStack.high - ## if length > 0: - ## let matchStr = s.substr(start, start+length-1) - ## case p.nt.name - ## of "Value": - ## try: - ## valStack.add matchStr.parseFloat - ## echo valStack - ## except ValueError: - ## discard - ## of "Sum", "Product": - ## try: - ## let val = matchStr.parseFloat - ## except ValueError: - ## if valStack.len > 1 and opStack.len > 0: - ## valStack[^2] = case opStack[^1] - ## of '+': valStack[^2] + valStack[^1] - ## of '-': valStack[^2] - valStack[^1] - ## of '*': valStack[^2] * valStack[^1] - ## else: valStack[^2] / valStack[^1] - ## valStack.setLen valStack.high - ## echo valStack - ## opStack.setLen opStack.high - ## echo opStack - ## pkChar: - ## leave: - ## if length == 1 and "Value" != pStack[^1]: - ## let matchChar = s[start] - ## opStack.add matchChar - ## echo opStack + ## var + ## pStack: seq[string] = @[] + ## valStack: seq[float] = @[] + ## opStack = "" + ## let + ## parseArithExpr = pegAst.eventParser: + ## pkNonTerminal: + ## enter: + ## pStack.add p.nt.name + ## leave: + ## pStack.setLen pStack.high + ## if length > 0: + ## let matchStr = s.substr(start, start+length-1) + ## case p.nt.name + ## of "Value": + ## try: + ## valStack.add matchStr.parseFloat + ## echo valStack + ## except ValueError: + ## discard + ## of "Sum", "Product": + ## try: + ## let val = matchStr.parseFloat + ## except ValueError: + ## if valStack.len > 1 and opStack.len > 0: + ## valStack[^2] = case opStack[^1] + ## of '+': valStack[^2] + valStack[^1] + ## of '-': valStack[^2] - valStack[^1] + ## of '*': valStack[^2] * valStack[^1] + ## else: valStack[^2] / valStack[^1] + ## valStack.setLen valStack.high + ## echo valStack + ## opStack.setLen opStack.high + ## echo opStack + ## pkChar: + ## leave: + ## if length == 1 and "Value" != pStack[^1]: + ## let matchChar = s[start] + ## opStack.add matchChar + ## echo opStack ## - ## let pLen = parseArithExpr(txt) + ## let pLen = parseArithExpr(txt) + ## ``` ## ## The *handlers* parameter consists of code blocks for *PegKinds*, ## which define the grammar elements of interest. Each block can contain @@ -1181,8 +1183,7 @@ template `=~`*(s: string, pattern: Peg): bool = ## This calls ``match`` with an implicit declared ``matches`` array that ## can be used in the scope of the ``=~`` call: ## - ## .. code-block:: nim - ## + ## ```nim ## if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": ## # matches a key=value pair: ## echo("Key: ", matches[0]) @@ -1194,7 +1195,7 @@ template `=~`*(s: string, pattern: Peg): bool = ## echo("comment: ", matches[0]) ## else: ## echo("syntax error") - ## + ## ``` bind MaxSubpatterns when not declaredInScope(matches): var matches {.inject.}: array[0..MaxSubpatterns-1, string] @@ -1230,14 +1231,15 @@ func replacef*(s: string, sub: Peg, by: string): string {. ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## - ## .. code-block:: nim + ## ```nim ## "var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") + ## ``` ## ## Results in: ## - ## .. code-block:: nim - ## + ## ```nim ## "var1<-keykey; val2<-key2key2" + ## ``` result = "" var i = 0 var caps: array[0..MaxSubpatterns-1, string] @@ -1305,8 +1307,7 @@ func replace*(s: string, sub: Peg, cb: proc( ## The callback proc receives the index of the current match (starting with 0), ## the count of captures and an open array with the captures of each match. Examples: ## - ## .. code-block:: nim - ## + ## ```nim ## func handleMatches*(m: int, n: int, c: openArray[string]): string = ## result = "" ## if m > 0: @@ -1318,12 +1319,13 @@ func replace*(s: string, sub: Peg, cb: proc( ## ## let s = "Var1=key1;var2=Key2; VAR3" ## echo s.replace(peg"{\ident}('='{\ident})* ';'* \s*", handleMatches) + ## ``` ## ## Results in: ## - ## .. code-block:: nim - ## + ## ```nim ## "var1: 'key1', var2: 'Key2', var3: ''" + ## ``` result = "" var i = 0 var caps: array[0..MaxSubpatterns-1, string] @@ -1361,18 +1363,19 @@ iterator split*(s: string, sep: Peg): string = ## Substrings are separated by the PEG `sep`. ## Examples: ## - ## .. code-block:: nim + ## ```nim ## for word in split("00232this02939is39an22example111", peg"\d+"): ## writeLine(stdout, word) + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ```nim ## "this" ## "is" ## "an" ## "example" - ## + ## ``` var c: Captures var first = 0 diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index fcee22c09ff4..1b4ae992de14 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -205,12 +205,11 @@ when defined(nimdoc): ## to `value`. This `value` can be modified in the scope of ## the `withData` call. ## - ## .. code-block:: nim - ## + ## ```nim ## s.withData(fd, value) do: ## # block is executed only if `fd` registered in selector `s` ## value.uid = 1000 - ## + ## ``` template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, body2: untyped) = @@ -218,15 +217,14 @@ when defined(nimdoc): ## to `value`. This `value` can be modified in the scope of ## the `withData` call. ## - ## .. code-block:: nim - ## + ## ```nim ## s.withData(fd, value) do: ## # block is executed only if `fd` registered in selector `s`. ## value.uid = 1000 ## do: ## # block is executed if `fd` not registered in selector `s`. ## raise - ## + ## ``` proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = ## Determines whether selector contains a file descriptor. diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index e18e2d43a65d..d3aeacee55ca 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -27,67 +27,67 @@ ## StringStream example ## -------------------- ## -## .. code-block:: Nim +## ```Nim +## import std/streams ## -## import std/streams +## var strm = newStringStream("""The first line +## the second line +## the third line""") ## -## var strm = newStringStream("""The first line -## the second line -## the third line""") +## var line = "" ## -## var line = "" +## while strm.readLine(line): +## echo line ## -## while strm.readLine(line): -## echo line +## # Output: +## # The first line +## # the second line +## # the third line ## -## # Output: -## # The first line -## # the second line -## # the third line -## -## strm.close() +## strm.close() +## ``` ## ## FileStream example ## ------------------ ## ## Write file stream example: ## -## .. code-block:: Nim -## -## import std/streams +## ```Nim +## import std/streams ## -## var strm = newFileStream("somefile.txt", fmWrite) -## var line = "" +## var strm = newFileStream("somefile.txt", fmWrite) +## var line = "" ## -## if not isNil(strm): -## strm.writeLine("The first line") -## strm.writeLine("the second line") -## strm.writeLine("the third line") -## strm.close() +## if not isNil(strm): +## strm.writeLine("The first line") +## strm.writeLine("the second line") +## strm.writeLine("the third line") +## strm.close() ## -## # Output (somefile.txt): -## # The first line -## # the second line -## # the third line +## # Output (somefile.txt): +## # The first line +## # the second line +## # the third line +## ``` ## ## Read file stream example: ## -## .. code-block:: Nim +## ```Nim +## import std/streams ## -## import std/streams +## var strm = newFileStream("somefile.txt", fmRead) +## var line = "" ## -## var strm = newFileStream("somefile.txt", fmRead) -## var line = "" +## if not isNil(strm): +## while strm.readLine(line): +## echo line +## strm.close() ## -## if not isNil(strm): -## while strm.readLine(line): -## echo line -## strm.close() -## -## # Output: -## # The first line -## # the second line -## # the third line +## # Output: +## # The first line +## # the second line +## # the third line +## ``` ## ## See also ## ======== @@ -348,9 +348,9 @@ proc write*[T](s: Stream, x: T) = ## **Note:** Not available for JS backend. Use `write(Stream, string) ## <#write,Stream,string>`_ for now. ## - ## .. code-block:: Nim - ## - ## s.writeData(s, unsafeAddr(x), sizeof(x)) + ## ```Nim + ## s.writeData(s, unsafeAddr(x), sizeof(x)) + ## ``` runnableExamples: var strm = newStringStream("") strm.write("abcde") diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim index a6c1901d2b96..9f5c0f28ae09 100644 --- a/lib/pure/streamwrapper.nim +++ b/lib/pure/streamwrapper.nim @@ -91,14 +91,14 @@ proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] = ## when setPosition/getPosition is called or write operation is performed. ## ## Example: - ## - ## .. code-block:: Nim + ## ```Nim ## import std/[osproc, streamwrapper] ## var ## p = startProcess(exePath) ## outStream = p.outputStream().newPipeOutStream() ## echo outStream.peekChar ## p.close() + ## ``` assert s.readDataImpl != nil diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 1cebefee102c..2668ad66ca8c 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -133,13 +133,14 @@ runnableExamples: An expression like `&"{key} is {value:arg} {{z}}"` is transformed into: -.. code-block:: nim + ```nim var temp = newStringOfCap(educatedCapGuess) temp.formatValue(key, "") temp.add(" is ") temp.formatValue(value, arg) temp.add(" {z}") temp + ``` Parts of the string that are enclosed in the curly braces are interpreted as Nim code. To escape a `{` or `}`, double it. @@ -272,13 +273,14 @@ The available floating point presentation types are: Because of the well defined order how templates and macros are expanded, strformat cannot expand template arguments: -.. code-block:: nim + ```nim template myTemplate(arg: untyped): untyped = echo "arg is: ", arg echo &"--- {arg} ---" let x = "abc" myTemplate(x) + ``` First the template `myTemplate` is expanded, where every identifier `arg` is substituted with its argument. The `arg` inside the @@ -289,12 +291,13 @@ identifier that cannot be resolved anymore. The workaround for this is to bind the template argument to a new local variable. -.. code-block:: nim + ```nim template myTemplate(arg: untyped): untyped = block: let arg1 {.inject.} = arg echo "arg is: ", arg1 echo &"--- {arg1} ---" + ``` The use of `{.inject.}` here is necessary again because of template expansion order and hygienic templates. But since we generally want to diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 8a1ea125fa7b..775c4244ac11 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -12,7 +12,7 @@ This module contains a `scanf`:idx: macro that can be used for extracting substrings from an input string. This is often easier than regular expressions. Some examples as an appetizer: -.. code-block:: nim + ```nim # check if input string matches a triple of integers: const input = "(1,2,4)" var x, y, z: int @@ -26,6 +26,7 @@ Some examples as an appetizer: var myfloat: float if scanf(input, "$i-$i-$i $w$s$f", year, month, day, identifier, myfloat): echo "yes, we have a match!" + ``` As can be seen from the examples, strings are matched verbatim except for substrings starting with ``$``. These constructions are available: @@ -83,8 +84,7 @@ matches optional tokens without any result binding. In this example, we define a helper proc ``someSep`` that skips some separators which we then use in our scanf pattern to help us in the matching process: -.. code-block:: nim - + ```nim proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int = # Note: The parameters and return value must match to what ``scanf`` requires result = 0 @@ -92,11 +92,11 @@ which we then use in our scanf pattern to help us in the matching process: if scanf(input, "$w$[someSep]$w", key, value): ... + ``` It also possible to pass arguments to a user definable matcher: -.. code-block:: nim - + ```nim proc ndigits(input: string; intVal: var int; start: int; n: int): int = # matches exactly ``n`` digits. Matchers need to return 0 if nothing # matched or otherwise the number of processed chars. @@ -115,6 +115,7 @@ It also possible to pass arguments to a user definable matcher: var year, month, day: int if scanf("2013-01-03", "${ndigits(4)}-${ndigits(2)}-${ndigits(2)}$.", year, month, day): ... + ``` The scanp macro @@ -145,8 +146,7 @@ not implemented. Simple example that parses the ``/etc/passwd`` file line by line: -.. code-block:: nim - + ```nim const etc_passwd = """root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh @@ -165,6 +165,7 @@ Simple example that parses the ``/etc/passwd`` file line by line: result.add entry else: break + ``` The ``scanp`` maps the grammar code into Nim code that performs the parsing. The parsing is performed with the help of 3 helper templates that that can be @@ -173,8 +174,7 @@ implemented for a custom type. These templates need to be named ``atom`` and ``nxt``. ``atom`` should be overloaded to handle both single characters and sets of character. -.. code-block:: nim - + ```nim import std/streams template atom(input: Stream; idx: int; c: char): bool = @@ -190,11 +190,11 @@ overloaded to handle both single characters and sets of character. if scanp(content, idx, +( ~{'\L', '\0'} -> entry.add(peekChar($input))), '\L'): result.add entry + ``` Calling ordinary Nim procs inside the macro is possible: -.. code-block:: nim - + ```nim proc digits(s: string; intVal: var int; start: int): int = var x = 0 while result+start < s.len and s[result+start] in {'0'..'9'} and s[result+start] != ':': @@ -220,12 +220,12 @@ Calling ordinary Nim procs inside the macro is possible: result.add login & " " & homedir else: break + ``` When used for matching, keep in mind that likewise scanf, no backtracking is performed. -.. code-block:: nim - + ```nim proc skipUntil(s: string; until: string; unless = '\0'; start: int): int = # Skips all characters until the string `until` is found. Returns 0 # if the char `unless` is found first or the end is reached. @@ -256,12 +256,12 @@ is performed. for r in collectLinks(body): echo r + ``` In this example both macros are combined seamlessly in order to maximise efficiency and perform different checks. -.. code-block:: nim - + ```nim iterator parseIps*(soup: string): string = ## ipv4 only! const digits = {'0'..'9'} @@ -279,7 +279,7 @@ efficiency and perform different checks. yield buf buf.setLen(0) # need to clear `buf` each time, cause it might contain garbage idx.inc - + ``` ]## diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 6c1e495648db..7ab3d37c80a7 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -129,11 +129,11 @@ const ## Not very useful by its own, you can use it to create *inverted* sets to ## make the `find func<#find,string,set[char],Natural,int>`_ ## find **invalid** characters in strings. Example: - ## - ## .. code-block:: nim + ## ```nim ## let invalid = AllChars - Digits ## doAssert "01234".find(invalid) == -1 ## doAssert "01A34".find(invalid) == 2 + ## ``` func isAlphaAscii*(c: char): bool {.rtl, extern: "nsuIsAlphaAsciiChar".} = ## Checks whether or not character `c` is alphabetical. @@ -423,14 +423,12 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string = ## ## Substrings are separated by the character `sep`. ## The code: - ## - ## .. code-block:: nim + ## ```nim ## for word in split(";;this;is;an;;example;;;", ';'): ## writeLine(stdout, word) - ## + ## ``` ## Results in: - ## - ## .. code-block:: + ## ``` ## "" ## "" ## "this" @@ -441,6 +439,7 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string = ## "" ## "" ## "" + ## ``` ## ## See also: ## * `rsplit iterator<#rsplit.i,string,char,int>`_ @@ -455,41 +454,46 @@ iterator split*(s: string, seps: set[char] = Whitespace, ## ## Substrings are separated by a substring containing only `seps`. ## - ## .. code-block:: nim + ## ```nim ## for word in split("this\lis an\texample"): ## writeLine(stdout, word) + ## ``` ## ## ...generates this output: ## - ## .. code-block:: + ## ``` ## "this" ## "is" ## "an" ## "example" + ## ``` ## ## And the following code: ## - ## .. code-block:: nim + ## ```nim ## for word in split("this:is;an$example", {';', ':', '$'}): ## writeLine(stdout, word) + ## ``` ## ## ...produces the same output as the first example. The code: ## - ## .. code-block:: nim + ## ```nim ## let date = "2012-11-20T22:08:08.398990" ## let separators = {' ', '-', ':', 'T'} ## for number in split(date, separators): ## writeLine(stdout, number) + ## ``` ## ## ...results in: ## - ## .. code-block:: + ## ``` ## "2012" ## "11" ## "20" ## "22" ## "08" ## "08.398990" + ## ``` ## ## .. note:: Empty separator set results in returning an original string, ## following the interpretation "split by no element". @@ -507,16 +511,18 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string = ## Substrings are separated by the string `sep`. ## The code: ## - ## .. code-block:: nim + ## ```nim ## for word in split("thisDATAisDATAcorrupted", "DATA"): ## writeLine(stdout, word) + ## ``` ## ## Results in: ## - ## .. code-block:: + ## ``` ## "this" ## "is" ## "corrupted" + ## ``` ## ## .. note:: Empty separator string results in returning an original string, ## following the interpretation "split by no element". @@ -561,15 +567,17 @@ iterator rsplit*(s: string, sep: char, ## string separator. Works exactly the same as `split iterator ## <#split.i,string,char,int>`_ except in reverse order. ## - ## .. code-block:: nim + ## ```nim ## for piece in "foo:bar".rsplit(':'): ## echo piece + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ``` ## "bar" ## "foo" + ## ``` ## ## Substrings are separated from the right by the char `sep`. ## @@ -586,15 +594,17 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace, ## string separator. Works exactly the same as `split iterator ## <#split.i,string,char,int>`_ except in reverse order. ## - ## .. code-block:: nim + ## ```nim ## for piece in "foo bar".rsplit(WhiteSpace): ## echo piece + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ``` ## "bar" ## "foo" + ## ``` ## ## Substrings are separated from the right by the set of chars `seps` ## @@ -614,15 +624,17 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1, ## string separator. Works exactly the same as `split iterator ## <#split.i,string,string,int>`_ except in reverse order. ## - ## .. code-block:: nim + ## ```nim ## for piece in "foothebar".rsplit("the"): ## echo piece + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ``` ## "bar" ## "foo" + ## ``` ## ## Substrings are separated from the right by the string `sep` ## @@ -648,13 +660,14 @@ iterator splitLines*(s: string, keepEol = false): string = ## ## Example: ## - ## .. code-block:: nim + ## ```nim ## for line in splitLines("\nthis\nis\nan\n\nexample\n"): ## writeLine(stdout, line) + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ```nim ## "" ## "this" ## "is" @@ -662,6 +675,7 @@ iterator splitLines*(s: string, keepEol = false): string = ## "" ## "example" ## "" + ## ``` ## ## See also: ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ @@ -694,16 +708,17 @@ iterator splitWhitespace*(s: string, maxsplit: int = -1): string = ## ## The following code: ## - ## .. code-block:: nim + ## ```nim ## let s = " foo \t bar baz " ## for ms in [-1, 1, 2, 3]: ## echo "------ maxsplit = ", ms, ":" ## for item in s.splitWhitespace(maxsplit=ms): ## echo '"', item, '"' + ## ``` ## ## ...results in: ## - ## .. code-block:: + ## ``` ## ------ maxsplit = -1: ## "foo" ## "bar" @@ -719,6 +734,7 @@ iterator splitWhitespace*(s: string, maxsplit: int = -1): string = ## "foo" ## "bar" ## "baz" + ## ``` ## ## See also: ## * `splitLines iterator<#splitLines.i,string>`_ @@ -797,13 +813,15 @@ func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl, ## For example, if a system had `#` as a delimiter, you could ## do the following to get the tail of the path: ## - ## .. code-block:: nim + ## ```nim ## var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1) + ## ``` ## ## Results in `tailSplit` containing: ## - ## .. code-block:: nim + ## ```nim ## @["Root#Object#Method", "Index"] + ## ``` ## ## See also: ## * `rsplit iterator <#rsplit.i,string,char,int>`_ @@ -825,13 +843,15 @@ func rsplit*(s: string, seps: set[char] = Whitespace, ## For example, if a system had `#` as a delimiter, you could ## do the following to get the tail of the path: ## - ## .. code-block:: nim + ## ```nim ## var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1) + ## ``` ## ## Results in `tailSplit` containing: ## - ## .. code-block:: nim + ## ```nim ## @["Root#Object#Method", "Index"] + ## ``` ## ## .. note:: Empty separator set results in returning an original string, ## following the interpretation "split by no element". @@ -855,13 +875,15 @@ func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl, ## For example, if a system had `#` as a delimiter, you could ## do the following to get the tail of the path: ## - ## .. code-block:: nim + ## ```nim ## var tailSplit = rsplit("Root#Object#Method#Index", "#", maxsplit=1) + ## ``` ## ## Results in `tailSplit` containing: ## - ## .. code-block:: nim + ## ```nim ## @["Root#Object#Method", "Index"] + ## ``` ## ## .. note:: Empty separator string results in returning an original string, ## following the interpretation "split by no element". @@ -1786,8 +1808,9 @@ func addSep*(dest: var string, sep = ", ", startLen: Natural = 0) {.inline.} = ## ## A shorthand for: ## - ## .. code-block:: nim + ## ```nim ## if dest.len > startLen: add(dest, sep) + ## ``` ## ## This is often useful for generating some code where the items need to ## be *separated* by `sep`. `sep` is only added if `dest` is longer than @@ -2634,13 +2657,13 @@ func formatEng*(f: BiggestFloat, ## decimal point or (if `trim` is true) the maximum number of digits to be ## shown. ## - ## .. code-block:: nim - ## + ## ```nim ## formatEng(0, 2, trim=false) == "0.00" ## formatEng(0, 2) == "0" ## formatEng(0.053, 0) == "53e-3" ## formatEng(52731234, 2) == "52.73e6" ## formatEng(-52731234, 2) == "-52.73e6" + ## ``` ## ## If `siPrefix` is set to true, the number will be displayed with the SI ## prefix corresponding to the exponent. For example 4100 will be displayed @@ -2655,8 +2678,7 @@ func formatEng*(f: BiggestFloat, ## different to appending the unit to the result as the location of the space ## is altered depending on whether there is an exponent. ## - ## .. code-block:: nim - ## + ## ```nim ## formatEng(4100, siPrefix=true, unit="V") == "4.1 kV" ## formatEng(4.1, siPrefix=true, unit="V") == "4.1 V" ## formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space @@ -2666,6 +2688,7 @@ func formatEng*(f: BiggestFloat, ## formatEng(4100) == "4.1e3" ## formatEng(4100, unit="V") == "4.1e3 V" ## formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " # Space with useUnitSpace=true + ## ``` ## ## `decimalSep` is used as the decimal separator. ## @@ -2829,13 +2852,15 @@ func `%`*(formatstr: string, a: openArray[string]): string {.rtl, ## ## This is best explained by an example: ## - ## .. code-block:: nim + ## ```nim ## "$1 eats $2." % ["The cat", "fish"] + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ```nim ## "The cat eats fish." + ## ``` ## ## The substitution variables (the thing after the `$`) are enumerated ## from 1 to `a.len`. @@ -2843,21 +2868,24 @@ func `%`*(formatstr: string, a: openArray[string]): string {.rtl, ## The notation `$#` can be used to refer to the next substitution ## variable: ## - ## .. code-block:: nim + ## ```nim ## "$# eats $#." % ["The cat", "fish"] + ## ``` ## ## Substitution variables can also be words (that is ## `[A-Za-z_]+[A-Za-z0-9_]*`) in which case the arguments in `a` with even ## indices are keys and with odd indices are the corresponding values. ## An example: ## - ## .. code-block:: nim + ## ```nim ## "$animal eats $food." % ["animal", "The cat", "food", "fish"] + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ```nim ## "The cat eats fish." + ## ``` ## ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is ## raised if an ill-formed format string has been passed to the `%` operator. @@ -2955,13 +2983,14 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ ## Substrings are separated by a substring containing only `seps`. ## Example: ## - ## .. code-block:: nim + ## ```nim ## for word in tokenize(" this is an example "): ## writeLine(stdout, word) + ## ``` ## ## Results in: ## - ## .. code-block:: nim + ## ```nim ## (" ", true) ## ("this", false) ## (" ", true) @@ -2971,6 +3000,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ ## (" ", true) ## ("example", false) ## (" ", true) + ## ``` var i = 0 while true: var j = i diff --git a/lib/pure/times.nim b/lib/pure/times.nim index f5775e4d951a..9c32c7b2113f 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -20,7 +20,7 @@ Examples ======== - .. code-block:: nim + ```nim import std/[times, os] # Simple benchmarking let time = cpuTime() @@ -37,6 +37,7 @@ # Arithmetic using TimeInterval echo "One year from now : ", now() + 1.years echo "One month from now : ", now() + 1.months + ``` Parsing and Formatting Dates ============================ @@ -44,10 +45,10 @@ The `DateTime` type can be parsed and formatted using the different `parse` and `format` procedures. - .. code-block:: nim - + ```nim let dt = parse("2000-01-01", "yyyy-MM-dd") echo dt.format("yyyy-MM-dd") + ``` The different format patterns that are supported are documented below. @@ -652,11 +653,10 @@ template eqImpl(a: Duration|Time, b: Duration|Time): bool = const DurationZero* = Duration() ## \ ## Zero value for durations. Useful for comparisons. - ## - ## .. code-block:: nim - ## + ## ```nim ## doAssert initDuration(seconds = 1) > DurationZero ## doAssert initDuration(seconds = 0) == DurationZero + ## ``` proc initDuration*(nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days, weeks: int64 = 0): Duration = diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 3b3684789b91..a6b01d64473c 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -34,9 +34,9 @@ ## ## Specify the test name as a command line argument. ## -## .. code:: -## +## ```cmd ## nim c -r test "my test name" "another test" +## ``` ## ## Multiple arguments can be used. ## @@ -45,9 +45,9 @@ ## ## Specify the suite name delimited by `"::"`. ## -## .. code:: -## +## ```cmd ## nim c -r test "my test name::" +## ``` ## ## Selecting tests by pattern ## ========================== @@ -58,19 +58,18 @@ ## ## Tests matching **any** of the arguments are executed. ## -## .. code:: -## +## ```cmd ## nim c -r test fast_suite::mytest1 fast_suite::mytest2 ## nim c -r test "fast_suite::mytest*" ## nim c -r test "auth*::" "crypto::hashing*" ## # Run suites starting with 'bug #' and standalone tests starting with '#' ## nim c -r test 'bug #*::' '::#*' +## ``` ## ## Examples ## ======== ## -## .. code:: nim -## +## ```nim ## suite "description for this stuff": ## echo "suite setup: run once before the tests" ## @@ -96,6 +95,7 @@ ## discard v[4] ## ## echo "suite teardown: run once after the tests" +## ``` ## ## Limitations/Bugs ## ================ @@ -473,27 +473,26 @@ template suite*(name, body) {.dirty.} = ## common fixture (``setup``, ``teardown``). The fixture is executed ## for EACH test. ## - ## .. code-block:: nim - ## suite "test suite for addition": - ## setup: - ## let result = 4 + ## ```nim + ## suite "test suite for addition": + ## setup: + ## let result = 4 ## - ## test "2 + 2 = 4": - ## check(2+2 == result) + ## test "2 + 2 = 4": + ## check(2+2 == result) ## - ## test "(2 + -2) != 4": - ## check(2 + -2 != result) + ## test "(2 + -2) != 4": + ## check(2 + -2 != result) ## - ## # No teardown needed + ## # No teardown needed + ## ``` ## ## The suite will run the individual test cases in the order in which ## they were listed. With default global settings the above code prints: ## - ## .. code-block:: - ## - ## [Suite] test suite for addition - ## [OK] 2 + 2 = 4 - ## [OK] (2 + -2) != 4 + ## [Suite] test suite for addition + ## [OK] 2 + 2 = 4 + ## [OK] (2 + -2) != 4 bind formatters, ensureInitialized, suiteEnded block: @@ -528,17 +527,15 @@ when not declared(setProgramResult): template test*(name, body) {.dirty.} = ## Define a single test case identified by `name`. ## - ## .. code-block:: nim - ## - ## test "roses are red": - ## let roses = "red" - ## check(roses == "red") + ## ```nim + ## test "roses are red": + ## let roses = "red" + ## check(roses == "red") + ## ``` ## ## The above code outputs: ## - ## .. code-block:: - ## - ## [OK] roses are red + ## [OK] roses are red bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName, setProgramResult ensureInitialized() @@ -585,11 +582,11 @@ proc checkpoint*(msg: string) = ## Set a checkpoint identified by `msg`. Upon test failure all ## checkpoints encountered so far are printed out. Example: ## - ## .. code-block:: nim - ## - ## checkpoint("Checkpoint A") - ## check((42, "the Answer to life and everything") == (1, "a")) - ## checkpoint("Checkpoint B") + ## ```nim + ## checkpoint("Checkpoint A") + ## check((42, "the Answer to life and everything") == (1, "a")) + ## checkpoint("Checkpoint B") + ## ``` ## ## outputs "Checkpoint A" once it fails. checkpoints.add(msg) @@ -601,11 +598,11 @@ template fail* = ## failed (change exit code and test status). This template is useful ## for debugging, but is otherwise mostly used internally. Example: ## - ## .. code-block:: nim - ## - ## checkpoint("Checkpoint A") - ## complicatedProcInThread() - ## fail() + ## ```nim + ## checkpoint("Checkpoint A") + ## complicatedProcInThread() + ## fail() + ## ``` ## ## outputs "Checkpoint A" before quitting. bind ensureInitialized, setProgramResult @@ -633,11 +630,10 @@ template skip* = ## for reasons depending on outer environment, ## or certain application logic conditions or configurations. ## The test code is still executed. - ## - ## .. code-block:: nim - ## - ## if not isGLContextCreated(): - ## skip() + ## ```nim + ## if not isGLContextCreated(): + ## skip() + ## ``` bind checkpoints testStatusIMPL = TestStatus.SKIPPED diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 186da4df814b..e4cd407e2953 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -935,8 +935,9 @@ proc xmlConstructor(a: NimNode): NimNode = macro `<>`*(x: untyped): untyped = ## Constructor macro for XML. Example usage: ## - ## .. code-block:: nim + ## ```nim ## <>a(href="http://nim-lang.org", newText("Nim rules.")) + ## ``` ## ## Produces an XML tree for: ## diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim index 6788dacde2ed..e545ac599d52 100644 --- a/lib/std/cmdline.nim +++ b/lib/std/cmdline.nim @@ -163,11 +163,12 @@ when defined(nimdoc): ## ## **Examples:** ## - ## .. code-block:: nim + ## ```nim ## when declared(paramCount): ## # Use paramCount() here ## else: ## # Do something else! + ## ``` proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = ## Returns the `i`-th `command line argument`:idx: given to the application. @@ -195,11 +196,12 @@ when defined(nimdoc): ## ## **Examples:** ## - ## .. code-block:: nim + ## ```nim ## when declared(paramStr): ## # Use paramStr() here ## else: ## # Do something else! + ## ``` elif defined(nimscript): discard elif defined(nodejs): @@ -296,11 +298,12 @@ when declared(paramCount) or defined(nimdoc): ## ## **Examples:** ## - ## .. code-block:: nim + ## ```nim ## when declared(commandLineParams): ## # Use commandLineParams() here ## else: ## # Do something else! + ## ``` result = @[] for i in 1..paramCount(): result.add(paramStr(i)) diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index 55ace35001d3..f2d0d25cba55 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -19,7 +19,7 @@ const # Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c # Generates: -# .. code-block:: nim +# ```nim # var res = "" # for i in 0 .. 99: # if i < 10: @@ -27,6 +27,7 @@ const # else: # res.add $i # doAssert res == digits100 +# ``` proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = buf[pos] = digits100[2 * digits] diff --git a/lib/std/private/since.nim b/lib/std/private/since.nim index 1a6dd02cb4e2..720120f117c4 100644 --- a/lib/std/private/since.nim +++ b/lib/std/private/since.nim @@ -15,19 +15,19 @@ The emulation cannot be 100% faithful and to avoid adding too much complexity, template since*(version: (int, int), body: untyped) {.dirty.} = ## Evaluates `body` if the ``(NimMajor, NimMinor)`` is greater than ## or equal to `version`. Usage: - ## - ## .. code-block:: Nim + ## ```Nim ## proc fun*() {.since: (1, 3).} ## since (1, 3): fun() + ## ``` when (NimMajor, NimMinor) >= version: body template since*(version: (int, int, int), body: untyped) {.dirty.} = ## Evaluates `body` if ``(NimMajor, NimMinor, NimPatch)`` is greater than ## or equal to `version`. Usage: - ## - ## .. code-block:: Nim + ## ```Nim ## proc fun*() {.since: (1, 3, 1).} ## since (1, 3, 1): fun() + ## ``` when (NimMajor, NimMinor, NimPatch) >= version: body diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim index b53d6a5d35f2..41d46e58ae7e 100644 --- a/lib/std/socketstreams.nim +++ b/lib/std/socketstreams.nim @@ -31,37 +31,38 @@ ## Examples ## ======== ## -## .. code-block:: Nim -## import std/socketstreams +## ```Nim +## import std/socketstreams ## -## var -## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## stream = newReadSocketStream(socket) -## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST") -## echo stream.readLine() # Will call `recv` -## stream.setPosition(0) -## echo stream.readLine() # Will return the read line from the buffer -## stream.resetStream() # Buffer is now empty, position is 0 -## echo stream.readLine() # Will call `recv` again -## stream.close() # Closes the socket +## var +## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## stream = newReadSocketStream(socket) +## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST") +## echo stream.readLine() # Will call `recv` +## stream.setPosition(0) +## echo stream.readLine() # Will return the read line from the buffer +## stream.resetStream() # Buffer is now empty, position is 0 +## echo stream.readLine() # Will call `recv` again +## stream.close() # Closes the socket +## ``` ## -## .. code-block:: Nim +## ```Nim +## import std/socketstreams ## -## import std/socketstreams -## -## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## socket.connect("127.0.0.1", Port(12345)) -## var sendStream = newWriteSocketStream(socket) -## sendStream.write "NOM" -## sendStream.setPosition(1) -## echo sendStream.peekStr(2) # OM -## sendStream.write "I" -## sendStream.setPosition(0) -## echo sendStream.readStr(3) # NIM -## echo sendStream.getPosition() # 3 -## sendStream.flush() # This actually performs the writing to the socket -## sendStream.setPosition(1) -## sendStream.write "I" # Throws an error as we can't write into an already sent buffer +## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## socket.connect("127.0.0.1", Port(12345)) +## var sendStream = newWriteSocketStream(socket) +## sendStream.write "NOM" +## sendStream.setPosition(1) +## echo sendStream.peekStr(2) # OM +## sendStream.write "I" +## sendStream.setPosition(0) +## echo sendStream.readStr(3) # NIM +## echo sendStream.getPosition() # 3 +## sendStream.flush() # This actually performs the writing to the socket +## sendStream.setPosition(1) +## sendStream.write "I" # Throws an error as we can't write into an already sent buffer +## ``` import net, streams diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 2c1cf6f1d6d4..501a0d0fab21 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -12,27 +12,27 @@ ## Examples ## ======== ## -## .. code-block:: Nim +## ```Nim +## import std/locks ## -## import std/locks +## var +## thr: array[0..4, Thread[tuple[a,b: int]]] +## L: Lock ## -## var -## thr: array[0..4, Thread[tuple[a,b: int]]] -## L: Lock +## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = +## for i in interval.a..interval.b: +## acquire(L) # lock stdout +## echo i +## release(L) ## -## proc threadFunc(interval: tuple[a,b: int]) {.thread.} = -## for i in interval.a..interval.b: -## acquire(L) # lock stdout -## echo i -## release(L) +## initLock(L) ## -## initLock(L) +## for i in 0..high(thr): +## createThread(thr[i], threadFunc, (i*10, i*10+5)) +## joinThreads(thr) ## -## for i in 0..high(thr): -## createThread(thr[i], threadFunc, (i*10, i*10+5)) -## joinThreads(thr) -## -## deinitLock(L) +## deinitLock(L) +## ``` diff --git a/lib/system.nim b/lib/system.nim index 521380a57838..7b27f226a62f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2408,9 +2408,9 @@ when defined(nimV2): proc repr*[T, U](x: HSlice[T, U]): string = ## Generic `repr` operator for slices that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim - ## $(1 .. 5) == "1 .. 5" + ## ```Nim + ## $(1 .. 5) == "1 .. 5" + ## ``` result = repr(x.a) result.add(" .. ") result.add(repr(x.b)) diff --git a/lib/system/channels_builtin.nim b/lib/system/channels_builtin.nim index e04f2a0db196..088003e4b2eb 100644 --- a/lib/system/channels_builtin.nim +++ b/lib/system/channels_builtin.nim @@ -26,7 +26,7 @@ ## The following is a simple example of two different ways to use channels: ## blocking and non-blocking. ## -## .. code-block:: Nim +## ```Nim ## # Be sure to compile with --threads:on. ## # The channels and threads modules are part of system and should not be ## # imported. @@ -87,6 +87,7 @@ ## ## # Clean up the channel. ## chan.close() +## ``` ## ## Sample output ## ------------- @@ -113,7 +114,7 @@ ## using e.g. `system.allocShared0` and pass these pointers through thread ## arguments: ## -## .. code-block:: Nim +## ```Nim ## proc worker(channel: ptr Channel[string]) = ## let greeting = channel[].recv() ## echo greeting @@ -135,6 +136,7 @@ ## deallocShared(channel) ## ## localChannelExample() # "Hello from the main thread!" +## ``` when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index 4ff3d0ae6cf5..89a739d5a73a 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -41,9 +41,9 @@ proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.} proc `$`*(x: char): string {.magic: "CharToStr", noSideEffect.} ## The stringify operator for a character argument. Returns `x` ## converted to a string. - ## - ## .. code-block:: Nim + ## ```Nim ## assert $'c' == "c" + ## ``` proc `$`*(x: cstring): string {.magic: "CStrToStr", noSideEffect.} ## The stringify operator for a CString argument. Returns `x` @@ -67,19 +67,20 @@ proc `$`*(t: typedesc): string {.magic: "TypeTrait".} ## For more procedures dealing with `typedesc`, see ## `typetraits module <typetraits.html>`_. ## - ## .. code-block:: Nim + ## ```Nim ## doAssert $(typeof(42)) == "int" ## doAssert $(typeof("Foo")) == "string" ## static: doAssert $(typeof(@['A', 'B'])) == "seq[char]" + ## ``` proc `$`*[T: tuple](x: T): string = ## Generic `$` operator for tuples that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(23, 45) == "(23, 45)" ## $(a: 23, b: 45) == "(a: 23, b: 45)" ## $() == "()" + ## ``` tupleObjectDollar(result, x) when not defined(nimPreviewSlimSystem): @@ -108,25 +109,25 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string = proc `$`*[T](x: set[T]): string = ## Generic `$` operator for sets that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## ${23, 45} == "{23, 45}" + ## ``` collectionToString(x, "{", ", ", "}") proc `$`*[T](x: seq[T]): string = ## Generic `$` operator for seqs that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(@[23, 45]) == "@[23, 45]" + ## ``` collectionToString(x, "@[", ", ", "]") proc `$`*[T, U](x: HSlice[T, U]): string = ## Generic `$` operator for slices that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(1 .. 5) == "1 .. 5" + ## ``` result = $x.a result.add(" .. ") result.add($x.b) @@ -140,7 +141,7 @@ when not defined(nimNoArrayToString): proc `$`*[T](x: openArray[T]): string = ## Generic `$` operator for openarrays that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]" + ## ``` collectionToString(x, "[", ", ", "]") diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 7db2b65674ba..35ab2690355d 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -31,7 +31,7 @@ In Nim the compiler cannot always know if a reference is stored on the stack or not. This is caused by var parameters. Consider this example: -.. code-block:: Nim + ```Nim proc setRef(r: var ref TNode) = new(r) @@ -41,11 +41,12 @@ Consider this example: setRef(r) # here we should not update the reference counts, because # r is on the stack setRef(r.left) # here we should update the refcounts! + ``` We have to decide at runtime whether the reference is on the stack or not. The generated code looks roughly like this: -.. code-block:: C + ```C void setref(TNode** ref) { unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode))) } @@ -53,6 +54,7 @@ The generated code looks roughly like this: setRef(&r) setRef(&r->left) } + ``` Note that for systems with a continuous stack (which most systems have) the check whether the ref is on the stack is very cheap (only two diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 9ce475e5b19d..a2c897b04b56 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -89,10 +89,9 @@ proc patchFile*(package, filename, replacement: string) = ## The compiler also performs `path substitution <nimc.html#compiler-usage-commandminusline-switches>`_ on `replacement`. ## ## Example: - ## - ## .. code-block:: nim - ## + ## ```nim ## patchFile("stdlib", "asyncdispatch", "patches/replacement") + ## ``` discard proc getCommand*(): string = @@ -159,20 +158,18 @@ proc strip(s: string): string = template `--`*(key, val: untyped) = ## A shortcut for `switch <#switch,string,string>`_ ## Example: - ## - ## .. code-block:: nim - ## + ## ```nim ## --path:somePath # same as switch("path", "somePath") ## --path:"someOtherPath" # same as switch("path", "someOtherPath") + ## ``` switch(strip(astToStr(key)), strip(astToStr(val))) template `--`*(key: untyped) = ## A shortcut for `switch <#switch,string,string>`_ ## Example: - ## - ## .. code-block:: nim - ## + ## ```nim ## --listCmd # same as switch("listCmd") + ## ``` switch(strip(astToStr(key))) type @@ -341,12 +338,12 @@ template withDir*(dir: string; body: untyped): untyped = ## ## If you need a permanent change, use the `cd() <#cd,string>`_ proc. ## Usage example: - ## - ## .. code-block:: nim + ## ```nim ## # inside /some/path/ ## withDir "foo": ## # move to /some/path/foo/ ## # back in /some/path/ + ## ``` let curDir = getCurrentDir() try: cd(dir) @@ -391,10 +388,10 @@ when not defined(nimble): ## Defines a task. Hidden tasks are supported via an empty description. ## ## Example: - ## - ## .. code-block:: nim - ## task build, "default build is via the C backend": - ## setCommand "c" + ## ```nim + ## task build, "default build is via the C backend": + ## setCommand "c" + ## ``` ## ## For a task named `foo`, this template generates a `proc` named ## `fooTask`. This is useful if you need to call one task in @@ -402,13 +399,14 @@ when not defined(nimble): ## ## Example: ## - ## .. code-block:: nim - ## task foo, "foo": # > nim foo - ## echo "Running foo" # Running foo + ## ```nim + ## task foo, "foo": # > nim foo + ## echo "Running foo" # Running foo ## - ## task bar, "bar": # > nim bar - ## echo "Running bar" # Running bar - ## fooTask() # Running foo + ## task bar, "bar": # > nim bar + ## echo "Running bar" # Running bar + ## fooTask() # Running foo + ## ``` proc `name Task`*() = setCommand "nop" body diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index 3a40ac5ad7b2..f81c615e91f6 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -38,8 +38,9 @@ proc repr*(x: char): string {.noSideEffect, raises: [].} = ## repr for a character argument. Returns `x` ## converted to an escaped string. ## - ## .. code-block:: Nim + ## ```Nim ## assert repr('c') == "'c'" + ## ``` result.add '\'' # Elides string creations if not needed if x in {'\\', '\0'..'\31', '\127'..'\255'}: @@ -132,11 +133,11 @@ proc reprObject[T: tuple|object](res: var string, x: T) {.noSideEffect, raises: proc repr*[T: tuple|object](x: T): string {.noSideEffect, raises: [].} = ## Generic `repr` operator for tuples that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(23, 45) == "(23, 45)" ## $(a: 23, b: 45) == "(a: 23, b: 45)" ## $() == "()" + ## ``` when T is object: result = $typeof(x) reprObject(result, x) @@ -164,17 +165,17 @@ proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string {.noSi proc repr*[T](x: set[T]): string = ## Generic `repr` operator for sets that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## ${23, 45} == "{23, 45}" + ## ``` collectionToRepr(x, "{", ", ", "}") proc repr*[T](x: seq[T]): string = ## Generic `repr` operator for seqs that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(@[23, 45]) == "@[23, 45]" + ## ``` collectionToRepr(x, "@[", ", ", "]") proc repr*[T, IDX](x: array[IDX, T]): string = @@ -184,9 +185,9 @@ proc repr*[T, IDX](x: array[IDX, T]): string = proc repr*[T](x: openArray[T]): string = ## Generic `repr` operator for openarrays that is lifted from the components ## of `x`. Example: - ## - ## .. code-block:: Nim + ## ```Nim ## $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]" + ## ``` collectionToRepr(x, "[", ", ", "]") proc repr*[T](x: UncheckedArray[T]): string = diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index e659746eecaf..b94ddaa217e5 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -18,12 +18,13 @@ ## ## Build and test examples: ## -## .. code-block:: +## ```cmd ## ./bin/nim c -d:ssl -p:. -r tests/stdlib/tssl.nim ## ./bin/nim c -d:ssl --threads:on -p:. -r tests/stdlib/thttpclient_ssl.nim ## ./bin/nim c -d:ssl -p:. -r tests/untestable/tssl.nim ## ./bin/nim c -d:ssl -p:. --dynlibOverride:ssl --passl:-lcrypto --passl:-lssl -r tests/untestable/tssl.nim ## ./bin/nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:testament/lib --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim +## ``` # https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html # diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index bcf825665686..e5a73305b880 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -693,11 +693,11 @@ proc newRecordGen(ctx: Context; typ: TypRef): PNode = String `interpolation`:idx: / `format`:idx: inspired by Python's ``f``-strings. -.. code-block:: nim - - import strformat - let msg = "hello" - doAssert fmt"{msg}\n" == "hello\\n" + ```nim + import strformat + let msg = "hello" + doAssert fmt"{msg}\n" == "hello\\n" + ``` Because the literal is a raw string literal, the ``\n`` is not interpreted as an escape sequence. diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 50ae92a62bdd..7f78b7e566e8 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -699,11 +699,11 @@ proc newRecordGen(ctx: Context; typ: TypRef): PNode = String `interpolation`:idx: / `format`:idx: inspired by Python's ``f``-strings. -.. code-block:: nim - - import strformat - let msg = "hello" - doAssert fmt"{msg}\n" == "hello\\n" + ```nim + import strformat + let msg = "hello" + doAssert fmt"{msg}\n" == "hello\\n" + ``` Because the literal is a raw string literal, the ``\n`` is not interpreted as an escape sequence. diff --git a/testament/specs.nim b/testament/specs.nim index 744d1f75ac35..5107c9ed71ee 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -143,27 +143,26 @@ proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var T ## ## Can parse a single message for a line: ## - ## .. code-block:: nim - ## + ## ```nim ## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error ## ^ 'generic_proc' should be: 'genericProc' [Name] ]# + ## ``` ## ## Can parse multiple messages for a line when they are separated by ';': ## - ## .. code-block:: nim - ## + ## ```nim ## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error ## ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Error ## ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Error ## ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]# + ## ``` ## - ## .. code-block:: nim - ## + ## ```nim ## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error ## ^ 'generic_proc' should be: 'genericProc' [Name]; ## tt.Error ^ 'no_destroy' should be: 'nodestroy' [Name]; ## tt.Error ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]# - ## + ## ``` result = i + len(inlineErrorMarker) inc col, len(inlineErrorMarker) let msgLine = line diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index e2c1b0a04508..0ad57167bb8b 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -168,14 +168,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter = ## initialised with. The template figures out at compile time what field to ## assign the variable to, and thus you reduce code clutter and may use this ## to initialise single assignments variables in `let` blocks. Example: - ## - ## .. code-block:: nim + ## ```nim ## let ## parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41) ## parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123) ## # The following line doesn't compile due to ## # type mismatch: got <string> but expected 'int' ## #parsed_param3 = new_parsed_parameter(PK_INT, "231") + ## ``` var result {.gensym.}: Tparsed_parameter result.kind = tkind when tkind == PK_EMPTY: discard From 9296b45de44eb371da970021d89499f0be04456b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 15 Aug 2023 21:42:26 +0800 Subject: [PATCH 176/347] update test command of important packages (#22485) --- testament/important_packages.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 9016a0d3dc91..f831311beeda 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -46,7 +46,7 @@ pkg "BipBuffer" pkg "blscurve", allowFailure = true pkg "bncurve" pkg "brainfuck", "nim c -d:release -r tests/compile.nim" -pkg "bump", "nim c --gc:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump", allowFailure = true +pkg "bump", "nim c --mm:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump", allowFailure = true pkg "c2nim", "nim c testsuite/tester.nim" pkg "cascade" pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true @@ -55,7 +55,7 @@ pkg "chroma" pkg "chronicles", "nim c -o:chr -r chronicles.nim" pkg "chronos", "nim c -r -d:release tests/testall" pkg "cligen", "nim c --path:. -r cligen.nim" -pkg "combparser", "nimble test --gc:orc" +pkg "combparser", "nimble test --mm:orc" pkg "compactdict" pkg "comprehension", "nimble test", "https://github.com/alehander92/comprehension" pkg "cowstrings" @@ -75,7 +75,7 @@ pkg "glob" pkg "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" pkg "gittyup", "nimble test", "https://github.com/disruptek/gittyup", allowFailure = true pkg "gnuplot", "nim c gnuplot.nim" -# pkg "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" +# pkg "gram", "nim c -r --mm:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" # pending https://github.com/nim-lang/Nim/issues/16509 pkg "hts", "nim c -o:htss src/hts.nim" pkg "httpauth" @@ -91,7 +91,7 @@ pkg "lockfreequeues" pkg "macroutils" pkg "manu" pkg "markdown" -pkg "measuremancer", "nimble install -y unchained@#HEAD; nimble -y test" +pkg "measuremancer", "nimble testDeps; nimble -y test" # when unchained is version 0.3.7 or higher, use `nimble testDeps;` pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" @@ -121,7 +121,7 @@ pkg "nimsl" pkg "nimsvg" pkg "nimterop", "nimble minitest", url = "https://github.com/nim-lang/nimterop" pkg "nimwc", "nim c nimwc.nim", allowFailure = true -pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true +pkg "nimx", "nim c test/main.nim", allowFailure = true pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "testament r tests/common/tmodel.nim" pkg "npeg", "nimble testarc" @@ -145,7 +145,7 @@ pkg "RollingHash", "nim c -r tests/test_cyclichash.nim" pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" pkg "sdl1", "nim c -r src/sdl.nim" pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" -pkg "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" +pkg "sigv4", "nim c --mm:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" pkg "sim" pkg "smtp", "nimble compileExample" pkg "snip", "nimble test", "https://github.com/genotrance/snip" From 6c4e7835bf9c1fea608c1f9f55026095fcdd14b2 Mon Sep 17 00:00:00 2001 From: Jason Beetham <beefers331@gmail.com> Date: Tue, 15 Aug 2023 09:48:31 -0600 Subject: [PATCH 177/347] When in object handles procedure call again, fixes #22474 (#22480) Ping @narimiran please backport to the 2.0 line. --- compiler/semtypinst.nim | 2 +- tests/objects/twhen1.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 56b922fdafd2..d01ca28e4322 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -204,7 +204,7 @@ proc hasValuelessStatics(n: PNode): bool = a proc doThing(_: MyThing) ]# - if n.safeLen == 0: + if n.safeLen == 0 and n.kind != nkEmpty: # Some empty nodes can get in here n.typ == nil or n.typ.kind == tyStatic else: for x in n: diff --git a/tests/objects/twhen1.nim b/tests/objects/twhen1.nim index 5b8eea3f46dc..fe072a46b1e0 100644 --- a/tests/objects/twhen1.nim +++ b/tests/objects/twhen1.nim @@ -55,3 +55,35 @@ type x: int when (NimMajor, NimMinor) >= (1, 1): y: int +discard MyObject(x: 100, y: 200) + +block: # Ensure when evaluates properly in objects + type X[bits: static int] = object #22474 + when bits >= 256: + data32: byte + else: + data16: byte + + static: + discard X[255]().data16 + discard X[256]().data32 + + + type ComplexExprObject[S: static string, I: static int, Y: static auto] = object + when 'h' in S and I < 10 and Y isnot float: + a: int + elif I > 30: + b: int + elif typeof(Y) is float: + c: int + else: + d: int + + static: + discard ComplexExprObject["hello", 9, 300i32]().a + discard ComplexExprObject["", 40, 30f]().b + discard ComplexExprObject["", 20, float 30]().c + discard ComplexExprObject["", 20, ""]().d + + + From ade75a148332e670244a719202f7f0337d2e469a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 16 Aug 2023 05:31:44 +0800 Subject: [PATCH 178/347] fixes #22481; fixes `card` undefined misalignment behavior (#22484) * fixes `card` undefined misalignment behavior * Update lib/system/sets.nim --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- lib/system/sets.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 5f7c3e37b849..97431c296497 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -13,9 +13,11 @@ proc cardSetImpl(s: ptr UncheckedArray[uint8], len: int): int {.inline.} = var i = 0 result = 0 + var num = 0'u64 when defined(x86) or defined(amd64): while i < len - 8: - inc(result, countBits64((cast[ptr uint64](s[i].unsafeAddr))[])) + copyMem(addr num, addr s[i], 8) + inc(result, countBits64(num)) inc(i, 8) while i < len: From 940b1607b8459b4b7b7e20d316bec95c8de85809 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 16 Aug 2023 19:46:44 +0800 Subject: [PATCH 179/347] fixes #22357; don't sink elements of var tuple cursors (#22486) --- compiler/injectdestructors.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index aa6470d3493d..829076d4936c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -185,7 +185,9 @@ proc isCursor(n: PNode): bool = template isUnpackedTuple(n: PNode): bool = ## we move out all elements of unpacked tuples, ## hence unpacked tuples themselves don't need to be destroyed - (n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple) + ## except it's already a cursor + (n.kind == nkSym and n.sym.kind == skTemp and + n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags) proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) = var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">" From 299394d21a3b7f2af02c8cdede1dcd0cd5948a0e Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Thu, 17 Aug 2023 05:38:15 +0100 Subject: [PATCH 180/347] Fix `seq.capacity` (#22488) --- lib/system/seqs_v2.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index efc2919fd487..3a49142bf4bb 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -141,7 +141,7 @@ proc newSeq[T](s: var seq[T], len: Natural) = template capacityImpl(sek: NimSeqV2): int = - if sek.p != nil: (xu.p.cap and not strlitFlag) else: 0 + if sek.p != nil: (sek.p.cap and not strlitFlag) else: 0 func capacity*[T](self: seq[T]): int {.inline.} = ## Returns the current capacity of the seq. @@ -153,7 +153,7 @@ func capacity*[T](self: seq[T]): int {.inline.} = {.cast(noSideEffect).}: let sek = unsafeAddr self - result = capacityImpl(cast[ptr NimSeqV2](sek)[]) + result = capacityImpl(cast[ptr NimSeqV2[T]](sek)[]) {.pop.} # See https://github.com/nim-lang/Nim/issues/21401 From 60307cc373cf6bf794f4b3494f3cb8b983f2de43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Thu, 17 Aug 2023 11:20:22 +0100 Subject: [PATCH 181/347] updates manual with codegenDecl on params docs (#22333) * documents member * Update doc/manual_experimental.md Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com> --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com> --- doc/manual_experimental.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index b0ce775ee1dc..72e883cbc3fd 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2385,8 +2385,7 @@ Notice when calling a constructor in the section of a global variable initializa Member pragma ============= -Similar to the `constructor` and `virtual` pragmas, the `member` pragma can be used to attach a `proc` or `func` to a type in C++. -It is more flexible than `virtual` in the sense that it accepts not only names but also operators or destructors. +Like the `constructor` and `virtual` pragmas, the `member` pragma can be used to attach a procedure to a C++ type. It's more flexible than the `virtual` pragma in the sense that it accepts not only names but also operators and destructors. For example: @@ -2439,4 +2438,4 @@ proc `()`(f: NimFunctor; n:int) {.importcpp: "#(@)" .} NimFunctor()(1) ``` Notice we use the overload of `()` to have the same semantics in Nim, but on the `importcpp` we import the functor as a function. -This allows to easy interop with functions that accepts for example a `const` operator in its signature. \ No newline at end of file +This allows to easy interop with functions that accepts for example a `const` operator in its signature. From ee817557ecccf0562cb3e6d4f4c72dcef4fe5e64 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 17 Aug 2023 19:33:19 +0800 Subject: [PATCH 182/347] =?UTF-8?q?close=20#22748;=20cursorinference=20+?= =?UTF-8?q?=20-d:nimNoLentIterators=20results=20in=20err=E2=80=A6=20(#2249?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closed #22748; cursorinference + -d:nimNoLentIterators results in erroneous recursion --- tests/arc/t22478.nim | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/arc/t22478.nim diff --git a/tests/arc/t22478.nim b/tests/arc/t22478.nim new file mode 100644 index 000000000000..5373fa1616a0 --- /dev/null +++ b/tests/arc/t22478.nim @@ -0,0 +1,46 @@ +discard """ + matrix: "-d:nimNoLentIterators --mm:arc" + output: '''PUSH DATA: {"test.message":{"test":{"nested":"v1"}}}''' + joinable: false +""" + +# bug #22748 +import std/[json, typetraits, times] + +# publish + +proc publish*[T](payload: T) = + discard + +type MetricsPoint* = JsonNode + +proc push*(stat: string, data: JsonNode, usec: int64 = 0) = + let payload = newJObject() + + # this results in a infinite recursion unless we deepCopy() + payload[stat] = data #.deepCopy + + echo "PUSH DATA: ", payload + + publish[MetricsPoint](payload) + +var scopes {.threadvar.}: seq[JsonNode] + +type WithTimeCallback*[T] = proc(data: var JsonNode): T + +proc pushScoped*[T](metric: string, blk: WithTimeCallback[T]): T {.gcsafe.} = + scopes.add newJObject() + defer: discard scopes.pop() + + let stc = (cpuTime() * 1000_000).int64 + result = blk(scopes[^1]) + let dfc = (cpuTime() * 1000_000).int64 - stc + + push(metric, scopes[^1], dfc) + +# demo code + +discard pushScoped[int]("test.message") do (data: var JsonNode) -> int: + data["test"] = %*{ + "nested": "v1" + } \ No newline at end of file From 2e3d9cdbee6c5ca7c439cfedf8bcb9f0f232a960 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 17 Aug 2023 19:54:00 +0800 Subject: [PATCH 183/347] fixes #22441; build documentation for more modules in the checksums (#22453) Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com> --- tools/kochdocs.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 750f3dcd54a8..ce6b83980d32 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -167,6 +167,9 @@ pkgs/db_connector/src/db_connector/db_postgres.nim pkgs/db_connector/src/db_connector/db_sqlite.nim pkgs/checksums/src/checksums/md5.nim pkgs/checksums/src/checksums/sha1.nim +pkgs/checksums/src/checksums/sha2.nim +pkgs/checksums/src/checksums/sha3.nim +pkgs/checksums/src/checksums/bcrypt.nim """.splitWhitespace() officialPackagesListWithoutIndex = """ From 019b488e1fcf1782b4452dac8a31965e1a4becae Mon Sep 17 00:00:00 2001 From: Nan Xiao <nan@chinadtrace.org> Date: Thu, 17 Aug 2023 20:26:33 +0800 Subject: [PATCH 184/347] fixes syncio document (#22498) --- lib/std/syncio.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index a2a5c305b694..879301f8afac 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -359,12 +359,12 @@ proc getOsFileHandle*(f: File): FileHandle = when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(windows): proc setInheritable*(f: FileHandle, inheritable: bool): bool = - ## control whether a file handle can be inherited by child processes. Returns + ## Controls whether a file handle can be inherited by child processes. Returns ## `true` on success. This requires the OS file handle, which can be ## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_. ## ## This procedure is not guaranteed to be available for all platforms. Test for - ## availability with `declared() <system.html#declared,untyped>`. + ## availability with `declared() <system.html#declared,untyped>`_. when SupportIoctlInheritCtl: result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1 elif defined(freertos) or defined(zephyr): From fede75723824e06f59f23b38a9016d3f8cdf71db Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 17 Aug 2023 22:48:28 +0800 Subject: [PATCH 185/347] bump checksums (#22497) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 8e94b51507c7..571242a3631b 100644 --- a/koch.nim +++ b/koch.nim @@ -13,7 +13,7 @@ const # examples of possible values for repos: Head, ea82b54 NimbleStableCommit = "168416290e49023894fc26106799d6f1fc964a2d" # master AtlasStableCommit = "7b780811a168f3f32bff4822369dda46a7f87f9a" - ChecksumsStableCommit = "b4c73320253f78e3a265aec6d9e8feb83f97c77b" + ChecksumsStableCommit = "025bcca3915a1b9f19878cea12ad68f9884648fc" # examples of possible values for fusion: #head, #ea82b54, 1.2.3 FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014" From 98c39e8e571b95e7d9351c7115f897ce9af1a218 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Thu, 17 Aug 2023 19:52:28 +0300 Subject: [PATCH 186/347] cascade tyFromExpr in type conversions in generic bodies (#22499) fixes #22490, fixes #22491, adapts #22029 to type conversions --- compiler/semexprs.nim | 12 +++++++----- tests/statictypes/tgenericcomputedrange.nim | 8 ++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index df65b337125d..52d1f0628ea7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -311,7 +311,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool = let s = qualifiedLookUp(c, n, {}) result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned" -proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = +proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode = if n.len != 2: localError(c.config, n.info, "a type conversion takes exactly one argument") return n @@ -358,7 +358,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = if n[1].kind == nkExprEqExpr and targetType.skipTypes(abstractPtrs).kind == tyObject: localError(c.config, n.info, "object construction uses ':', not '='") - var op = semExprWithType(c, n[1]) + var op = semExprWithType(c, n[1], flags * {efDetermineType}) if op.kind == nkClosedSymChoice and op.len > 0 and op[0].sym.kind == skEnumField: # resolves overloadedable enums op = ambiguousSymChoice(c, n, op) @@ -373,7 +373,9 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = # here or needs to be overwritten too then. result.add op - if targetType.kind == tyGenericParam: + if targetType.kind == tyGenericParam or + (op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0): + # expression is compiled early in a generic body result.typ = makeTypeFromExpr(c, copyTree(result)) return result @@ -1075,7 +1077,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct}) if t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags, expectedType) - return semConv(c, n) + return semConv(c, n, flags) let nOrig = n.copyTree semOpAux(c, n) @@ -3123,7 +3125,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType # XXX think about this more (``set`` procs) let ambig = c.isAmbiguous if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2: - result = semConv(c, n, expectedType) + result = semConv(c, n, flags, expectedType) elif ambig and n.len == 1: errorUseQualifier(c, n.info, s) elif n.len == 1: diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim index 82abe267701a..9e3a49ae0888 100644 --- a/tests/statictypes/tgenericcomputedrange.nim +++ b/tests/statictypes/tgenericcomputedrange.nim @@ -115,3 +115,11 @@ block: # issue #22187 k: array[p(m(T, s)), int64] var x: F[int, 3] doAssert x.k is array[3, int64] + +block: # issue #22490 + proc log2trunc(x: uint64): int = + if x == 0: int(0) else: int(0) + template maxChunkIdx(T: typedesc): int64 = 0'i64 + template layer(vIdx: int64): int = log2trunc(0'u64) + type HashList[T] = object + indices: array[int(layer(maxChunkIdx(T))), int] From 7fababd583ee5e3c113c0d83a04c07f2ee0ef06d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 18 Aug 2023 00:52:38 +0800 Subject: [PATCH 187/347] make float32 literals stringifying behave in JS the same as in C (#22500) --- compiler/jsgen.nim | 9 +++++++-- tests/float/tfloats.nim | 5 ++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index c566a718a8e2..1720de17f899 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -32,7 +32,7 @@ import ast, trees, magicsys, options, nversion, msgs, idents, types, ropes, ccgutils, wordrecg, renderer, - cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, + cgmeth, lowerings, sighashes, modulegraphs, lineinfos, transf, injectdestructors, sourcemap, astmsgs, backendpragmas import pipelineutils @@ -43,6 +43,7 @@ import strutils except addf when defined(nimPreviewSlimSystem): import std/[assertions, syncio] +import std/formatfloat type TJSGen = object of PPassContext @@ -2900,7 +2901,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = rope"Infinity" of fcNegInf: r.res = rope"-Infinity" - else: r.res = rope(f.toStrMaxPrecision) + else: + if n.typ.skipTypes(abstractVarRange).kind == tyFloat32: + r.res.addFloatRoundtrip(f.float32) + else: + r.res.addFloatRoundtrip(f) r.kind = resExpr of nkCallKinds: if isEmptyType(n.typ): diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim index 967605c53d47..aaed2d615b6a 100644 --- a/tests/float/tfloats.nim +++ b/tests/float/tfloats.nim @@ -156,9 +156,8 @@ template main = when nimvm: discard # xxx, refs #12884 else: - when not defined(js): - doAssert x == 1.2345679'f32 - doAssert $x == "1.2345679" + doAssert x == 1.2345679'f32 + doAssert $x == "1.2345679" static: main() main() From eb83d20d0d1ab1d0cbd9574a3dc1bcdae949e865 Mon Sep 17 00:00:00 2001 From: Tomohiro <gpuppur@gmail.com> Date: Fri, 18 Aug 2023 23:47:47 +0900 Subject: [PATCH 188/347] Add staticFileExists and staticDirExists (#22278) --- compiler/vmops.nim | 4 ++++ lib/std/staticos.nim | 13 +++++++++++++ tests/stdlib/tstaticos.nim | 8 ++++++++ tests/vm/tvmmisc.nim | 2 ++ 4 files changed, 27 insertions(+) create mode 100644 lib/std/staticos.nim create mode 100644 tests/stdlib/tstaticos.nim diff --git a/compiler/vmops.nim b/compiler/vmops.nim index e81822ba6411..23b41fd2ee1c 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -264,6 +264,10 @@ proc registerAdditionalOps*(c: PCtx) = systemop getCurrentException registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) + registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} = + setResult(a, dirExists(getString(a, 0))) + registerCallback c, "stdlib.staticos.staticFileExists", proc (a: VmArgs) {.nimcall.} = + setResult(a, fileExists(getString(a, 0))) registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) = setResult(a, querySettingImpl(c.config, getInt(a, 0))) registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) = diff --git a/lib/std/staticos.nim b/lib/std/staticos.nim new file mode 100644 index 000000000000..2617c69138a9 --- /dev/null +++ b/lib/std/staticos.nim @@ -0,0 +1,13 @@ +## This module implements path handling like os module but works at only compile-time. +## This module works even when cross compiling to OS that is not supported by os module. + +proc staticFileExists*(filename: string): bool {.compileTime.} = + ## Returns true if `filename` exists and is a regular file or symlink. + ## + ## Directories, device files, named pipes and sockets return false. + discard + +proc staticDirExists*(dir: string): bool {.compileTime.} = + ## Returns true if the directory `dir` exists. If `dir` is a file, false + ## is returned. Follows symlinks. + discard diff --git a/tests/stdlib/tstaticos.nim b/tests/stdlib/tstaticos.nim new file mode 100644 index 000000000000..41ab995dd7ee --- /dev/null +++ b/tests/stdlib/tstaticos.nim @@ -0,0 +1,8 @@ +import std/[assertions, staticos, os] + +block: + static: + doAssert staticDirExists("MISSINGFILE") == false + doAssert staticFileExists("MISSINGDIR") == false + doAssert staticDirExists(currentSourcePath().parentDir) + doAssert staticFileExists(currentSourcePath()) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 1a8f68ee8f2e..da8208f73e92 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -82,6 +82,8 @@ block: doAssert fileExists("MISSINGFILE") == false doAssert dirExists("MISSINGDIR") == false + doAssert fileExists(currentSourcePath()) + doAssert dirExists(currentSourcePath().parentDir) # bug #7210 block: From 20cbdc2741e9e34a99288ded9664378afc80acb6 Mon Sep 17 00:00:00 2001 From: Alberto Torres <kungfoobar@gmail.com> Date: Fri, 18 Aug 2023 21:13:27 +0200 Subject: [PATCH 189/347] Fix #22366 by making nimlf_/nimln_ part of the same line (#22503) Fix #22366 by making nimlf_/nimln_ part of the same line so the debugger doesn't advance to the next line before executing it --- compiler/cgen.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 811f89983425..6d301dc475c7 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -312,10 +312,10 @@ proc genLineDir(p: BProc, t: PNode) = (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx: if freshLine: if lastFileIndex == t.info.fileIndex: - linefmt(p, cpsStmts, "nimln_($1);\n", + linefmt(p, cpsStmts, "nimln_($1);", [line]) else: - linefmt(p, cpsStmts, "nimlf_($1, $2);\n", + linefmt(p, cpsStmts, "nimlf_($1, $2);", [line, quotedFilename(p.config, t.info)]) proc accessThreadLocalVar(p: BProc, s: PSym) From c44c8ddb44b1379720f1f2c42bfa2dafe7fbc11c Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Sat, 19 Aug 2023 02:05:06 -0300 Subject: [PATCH 190/347] Remove Deprecated Babel (#22507) --- compiler/commands.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 749be80f0df7..6d162fab26bc 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -622,8 +622,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; for path in nimbleSubs(conf, arg): addPath(conf, if pass == passPP: processCfgPath(conf, path, info) else: processPath(conf, path, info), info) - of "nimblepath", "babelpath": - if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath") + of "nimblepath": if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions: expectArg(conf, switch, arg, pass, info) var path = processPath(conf, arg, info, notRelativeToProj=true) @@ -633,8 +632,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; nimblePath(conf, path, info) path = nimbleDir / RelativeDir"pkgs" nimblePath(conf, path, info) - of "nonimblepath", "nobabelpath": - if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath") + of "nonimblepath": expectNoArg(conf, switch, arg, pass, info) disableNimblePath(conf) of "clearnimblepath": From 6eb722c47d7010e83ab953a7f3a9ef5d1e624c56 Mon Sep 17 00:00:00 2001 From: Nan Xiao <nan@chinadtrace.org> Date: Sat, 19 Aug 2023 21:05:17 +0800 Subject: [PATCH 191/347] replace getOpt with getopt (#22515) --- tests/manyloc/keineschweine/dependencies/nake/nake.nim | 2 +- tests/manyloc/keineschweine/enet_server/enet_server.nim | 2 +- tests/manyloc/keineschweine/keineschweine.nim | 2 +- tests/manyloc/keineschweine/server/old_dirserver.nim | 2 +- tests/manyloc/keineschweine/server/old_sg_server.nim | 2 +- tests/manyloc/nake/nake.nim | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim index e466ee5e1f56..36538097e121 100644 --- a/tests/manyloc/keineschweine/dependencies/nake/nake.nim +++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim @@ -69,7 +69,7 @@ else: var task: string printTaskList: bool - for kind, key, val in getOpt(): + for kind, key, val in getopt(): case kind of cmdLongOption, cmdShortOption: case key.tolower diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim index 794e1c843948..86d0ab360b80 100644 --- a/tests/manyloc/keineschweine/enet_server/enet_server.nim +++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim @@ -113,7 +113,7 @@ when true: block: var zoneCfgFile = "./server_settings.json" - for kind, key, val in getOpt(): + for kind, key, val in getopt(): case kind of cmdShortOption, cmdLongOption: case key diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index cde31bd181f8..123a4aebbb64 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -691,7 +691,7 @@ when true: block: var bPlayOffline = false - for kind, key, val in getOpt(): + for kind, key, val in getopt(): case kind of cmdArgument: if key == "offline": bPlayOffline = true diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim index 70ae05b0bbce..a6382969137a 100644 --- a/tests/manyloc/keineschweine/server/old_dirserver.nim +++ b/tests/manyloc/keineschweine/server/old_dirserver.nim @@ -159,7 +159,7 @@ proc poll*(timeout: int = 250) = when true: import parseopt, strutils var cfgFile = "dirserver_settings.json" - for kind, key, val in getOpt(): + for kind, key, val in getopt(): case kind of cmdShortOption, cmdLongOption: case key diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim index 8bd44017ed5c..d6fbbe99ece8 100644 --- a/tests/manyloc/keineschweine/server/old_sg_server.nim +++ b/tests/manyloc/keineschweine/server/old_sg_server.nim @@ -144,7 +144,7 @@ proc poll*(timeout: int = 250) = when true: import parseopt, strutils var zoneCfgFile = "./server_settings.json" - for kind, key, val in getOpt(): + for kind, key, val in getopt(): case kind of cmdShortOption, cmdLongOption: case key diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim index ce7faab5cc03..5d3173a20056 100644 --- a/tests/manyloc/nake/nake.nim +++ b/tests/manyloc/nake/nake.nim @@ -65,7 +65,7 @@ else: var task: string printTaskList: bool - for kind, key, val in getOpt(): + for kind, key, val in getopt(): case kind of cmdLongOption, cmdShortOption: case key.tolowerAscii From d77ada5bdfe1a0778e37604078a38178fe6045f4 Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Sat, 19 Aug 2023 14:14:56 +0100 Subject: [PATCH 192/347] Markdown code blocks migration part 9 (#22506) * Markdown code blocks migration part 9 * fix [skip ci] --- compiler/astalgo.nim | 2 +- compiler/sigmatch.nim | 2 +- lib/core/macros.nim | 28 ++++----- lib/system.nim | 124 ++++++++++++++++++------------------- lib/system/compilation.nim | 14 ++--- lib/system/indices.nim | 12 ++-- 6 files changed, 91 insertions(+), 91 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 621bd23808b9..1873d231fe8c 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -227,7 +227,7 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = ## Named parameters are special because a named parameter can be ## gensym'ed and then they have '\`<number>' suffix that we need to ## ignore, see compiler / evaltempl.nim, snippet: - ## ``` + ## ```nim ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), ## if c.instLines: actual.info else: templ.info) ## ``` diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 048b74547f9d..7780e53f538b 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -576,7 +576,7 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = ## For example we have: - ## ``` + ## ```nim ## proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ... ## proc innerProc[Q,W](q: Q): W = ... ## ``` diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3286d7861f7e..01a654b6c675 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -208,7 +208,7 @@ template `or`*(x, y: NimNode): NimNode = ## Evaluate `x` and when it is not an empty node, return ## it. Otherwise evaluate to `y`. Can be used to chain several ## expressions to get the first expression that is not empty. - ## ``` + ## ```nim ## let node = mightBeEmpty() or mightAlsoBeEmpty() or fallbackNode ## ``` @@ -590,7 +590,7 @@ proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEf ## Obtains the AST nodes returned from a macro or template invocation. ## See also `genasts.genAst`. ## Example: - ## ``` + ## ```nim ## macro FooMacro() = ## var ast = getAst(BarTemplate()) ## ``` @@ -1049,7 +1049,7 @@ macro dumpTree*(s: untyped): untyped = echo s.treeRepr ## a certain expression/statement. ## ## For example: - ## ``` + ## ```nim ## dumpTree: ## echo "Hello, World!" ## ``` @@ -1073,7 +1073,7 @@ macro dumpLisp*(s: untyped): untyped = echo s.lispRepr(indented = true) ## a certain expression/statement. ## ## For example: - ## ``` + ## ```nim ## dumpLisp: ## echo "Hello, World!" ## ``` @@ -1096,7 +1096,7 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr ## outputs and then copying the snippets into the macro for modification. ## ## For example: - ## ``` + ## ```nim ## dumpAstGen: ## echo "Hello, World!" ## ``` @@ -1179,7 +1179,7 @@ proc newIdentDefs*(name, kind: NimNode; ## `let` or `var` blocks may have an empty `kind` node if the ## identifier is being assigned a value. Example: ## - ## ``` + ## ```nim ## var varSection = newNimNode(nnkVarSection).add( ## newIdentDefs(ident("a"), ident("string")), ## newIdentDefs(ident("b"), newEmptyNode(), newLit(3))) @@ -1190,7 +1190,7 @@ proc newIdentDefs*(name, kind: NimNode; ## ## If you need to create multiple identifiers you need to use the lower level ## `newNimNode`: - ## ``` + ## ```nim ## result = newNimNode(nnkIdentDefs).add( ## ident("a"), ident("b"), ident("c"), ident("string"), ## newStrLitNode("Hello")) @@ -1241,7 +1241,7 @@ proc newProc*(name = newEmptyNode(); proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode = ## Constructor for `if` statements. - ## ``` + ## ```nim ## newIfStmt( ## (Ident, StmtList), ## ... @@ -1258,7 +1258,7 @@ proc newEnum*(name: NimNode, fields: openArray[NimNode], ## Creates a new enum. `name` must be an ident. Fields are allowed to be ## either idents or EnumFieldDef: - ## ``` + ## ```nim ## newEnum( ## name = ident("Colors"), ## fields = [ident("Blue"), ident("Red")], @@ -1429,7 +1429,7 @@ iterator children*(n: NimNode): NimNode {.inline.} = template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = ## Find the first child node matching condition (or nil). - ## ``` + ## ```nim ## var res = findChild(n, it.kind == nnkPostfix and ## it.basename.ident == ident"foo") ## ``` @@ -1536,7 +1536,7 @@ macro expandMacros*(body: typed): untyped = ## ## For instance, ## - ## ``` + ## ```nim ## import std/[sugar, macros] ## ## let @@ -1652,7 +1652,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped = ## ## See also `getCustomPragmaVal`_. ## - ## ``` + ## ```nim ## template myAttr() {.pragma.} ## type ## MyObj = object @@ -1677,7 +1677,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## ## See also `hasCustomPragma`_. ## - ## ``` + ## ```nim ## template serializationKey(key: string) {.pragma.} ## type ## MyObj {.serializationKey: "mo".} = object @@ -1773,7 +1773,7 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = ## runnableExamples in `a`, stopping at the first child that is neither. ## Example: ## - ## ``` + ## ```nim ## import std/macros ## macro transf(a): untyped = ## result = quote do: diff --git a/lib/system.nim b/lib/system.nim index 7b27f226a62f..4163534cf629 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -108,7 +108,7 @@ proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## ## Cannot be overloaded. ## - ## ``` + ## ```nim ## var ## buf: seq[char] = @['a','b','c'] ## p = buf[1].addr @@ -194,7 +194,7 @@ proc high*[T: Ordinal|enum|range](x: T): T {.magic: "High", noSideEffect, ## **This proc is deprecated**, use this one instead: ## * `high(typedesc) <#high,typedesc[T]>`_ ## - ## ``` + ## ```nim ## high(2) # => 9223372036854775807 ## ``` @@ -202,7 +202,7 @@ proc high*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "High", noSideEffe ## Returns the highest possible value of an ordinal or enum type. ## ## `high(int)` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:. - ## ``` + ## ```nim ## high(int) # => 9223372036854775807 ## ``` ## @@ -211,7 +211,7 @@ proc high*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "High", noSideEffe proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.} ## Returns the highest possible index of a sequence `x`. - ## ``` + ## ```nim ## var s = @[1, 2, 3, 4, 5, 6, 7] ## high(s) # => 6 ## for i in low(s)..high(s): @@ -225,7 +225,7 @@ proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.} ## Returns the highest possible index of an array `x`. ## ## For empty arrays, the return type is `int`. - ## ``` + ## ```nim ## var arr = [1, 2, 3, 4, 5, 6, 7] ## high(arr) # => 6 ## for i in low(arr)..high(arr): @@ -239,7 +239,7 @@ proc high*[I, T](x: typedesc[array[I, T]]): I {.magic: "High", noSideEffect.} ## Returns the highest possible index of an array type. ## ## For empty arrays, the return type is `int`. - ## ``` + ## ```nim ## high(array[7, int]) # => 6 ## ``` ## @@ -255,7 +255,7 @@ proc high*(x: cstring): int {.magic: "High", noSideEffect.} proc high*(x: string): int {.magic: "High", noSideEffect.} ## Returns the highest possible index of a string `x`. - ## ``` + ## ```nim ## var str = "Hello world!" ## high(str) # => 11 ## ``` @@ -271,7 +271,7 @@ proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect, ## **This proc is deprecated**, use this one instead: ## * `low(typedesc) <#low,typedesc[T]>`_ ## - ## ``` + ## ```nim ## low(2) # => -9223372036854775808 ## ``` @@ -279,7 +279,7 @@ proc low*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "Low", noSideEffect ## Returns the lowest possible value of an ordinal or enum type. ## ## `low(int)` is Nim's way of writing `INT_MIN`:idx: or `MIN_INT`:idx:. - ## ``` + ## ```nim ## low(int) # => -9223372036854775808 ## ``` ## @@ -288,7 +288,7 @@ proc low*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "Low", noSideEffect proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of a sequence `x`. - ## ``` + ## ```nim ## var s = @[1, 2, 3, 4, 5, 6, 7] ## low(s) # => 0 ## for i in low(s)..high(s): @@ -302,7 +302,7 @@ proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of an array `x`. ## ## For empty arrays, the return type is `int`. - ## ``` + ## ```nim ## var arr = [1, 2, 3, 4, 5, 6, 7] ## low(arr) # => 0 ## for i in low(arr)..high(arr): @@ -316,7 +316,7 @@ proc low*[I, T](x: typedesc[array[I, T]]): I {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of an array type. ## ## For empty arrays, the return type is `int`. - ## ``` + ## ```nim ## low(array[7, int]) # => 0 ## ``` ## @@ -331,7 +331,7 @@ proc low*(x: cstring): int {.magic: "Low", noSideEffect.} proc low*(x: string): int {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of a string `x`. - ## ``` + ## ```nim ## var str = "Hello world!" ## low(str) # => 0 ## ``` @@ -409,7 +409,7 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag ## ## Slices can also be used in the set constructor and in ordinal case ## statements, but then they are special-cased by the compiler. - ## ``` + ## ```nim ## let a = [10, 20, 30, 40, 50] ## echo a[2 .. 3] # @[30, 40] ## ``` @@ -418,7 +418,7 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag proc `..`*[T](b: sink T): HSlice[int, T] {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} = ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`. - ## ``` + ## ```nim ## let a = [10, 20, 30, 40, 50] ## echo a[.. 2] # @[10, 20, 30] ## ``` @@ -573,7 +573,7 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} ## sizeof should fallback to the `sizeof` in the C compiler. The ## result isn't available for the Nim compiler and therefore can't ## be used inside of macros. - ## ``` + ## ```nim ## sizeof('A') # => 1 ## sizeof(2) # => 8 ## ``` @@ -604,7 +604,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## Note that the sequence will be filled with zeroed entries. ## After the creation of the sequence you should assign entries to ## the sequence instead of adding them. Example: - ## ``` + ## ```nim ## var inputStrings: seq[string] ## newSeq(inputStrings, 3) ## assert len(inputStrings) == 3 @@ -620,7 +620,7 @@ proc newSeq*[T](len = 0.Natural): seq[T] = ## Note that the sequence will be filled with zeroed entries. ## After the creation of the sequence you should assign entries to ## the sequence instead of adding them. - ## ``` + ## ```nim ## var inputStrings = newSeq[string](3) ## assert len(inputStrings) == 3 ## inputStrings[0] = "The fourth" @@ -638,7 +638,7 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {. magic: "NewSeqOfCap", noSideEffect.} = ## Creates a new sequence of type `seq[T]` with length zero and capacity ## `cap`. Example: - ## ``` + ## ```nim ## var x = newSeqOfCap[int](5) ## assert len(x) == 0 ## x.add(10) @@ -654,7 +654,7 @@ when not defined(js): ## uninitialized. After the creation of the sequence you should assign ## entries to the sequence instead of adding them. ## Example: - ## ``` + ## ```nim ## var x = newSeqUninitialized[int](3) ## assert len(x) == 3 ## x[0] = 10 @@ -748,7 +748,7 @@ include "system/setops" proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} = ## Checks if `value` is within the range of `s`; returns true if ## `value >= s.a and value <= s.b`. - ## ``` + ## ```nim ## assert((1..3).contains(1) == true) ## assert((1..3).contains(2) == true) ## assert((1..3).contains(4) == false) @@ -760,13 +760,13 @@ when not defined(nimHasCallsitePragma): template `in`*(x, y: untyped): untyped {.dirty, callsite.} = contains(y, x) ## Sugar for `contains`. - ## ``` + ## ```nim ## assert(1 in (1..3) == true) ## assert(5 in (1..3) == false) ## ``` template `notin`*(x, y: untyped): untyped {.dirty, callsite.} = not contains(y, x) ## Sugar for `not contains`. - ## ``` + ## ```nim ## assert(1 notin (1..3) == false) ## assert(5 notin (1..3) == true) ## ``` @@ -776,7 +776,7 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## ## For a negated version, use `isnot <#isnot.t,untyped,untyped>`_. ## - ## ``` + ## ```nim ## assert 42 is int ## assert @[1, 2] is seq ## @@ -791,7 +791,7 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} ## ``` template `isnot`*(x, y: untyped): untyped {.callsite.} = not (x is y) ## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`. - ## ``` + ## ```nim ## assert 42 isnot float ## assert @[1, 2] isnot enum ## ``` @@ -887,7 +887,7 @@ proc cmp*[T](x, y: T): int = ## ## This is useful for writing generic algorithms without performance loss. ## This generic implementation uses the `==` and `<` operators. - ## ``` + ## ```nim ## import std/algorithm ## echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int]) ## ``` @@ -908,7 +908,7 @@ proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEff ## sequences with the array constructor: `@[1, 2, 3]` has the type ## `seq[int]`, while `[1, 2, 3]` has the type `array[0..2, int]`. ## - ## ``` + ## ```nim ## let ## a = [1, 3, 5] ## b = "foo" @@ -950,7 +950,7 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {. ## ## If the current length is greater than the new length, ## `s` will be truncated. - ## ``` + ## ```nim ## var x = @[10, 20] ## x.setLen(5) ## x[4] = 50 @@ -965,7 +965,7 @@ proc setLen*(s: var string, newlen: Natural) {. ## ## If the current length is greater than the new length, ## `s` will be truncated. - ## ``` + ## ```nim ## var myS = "Nim is great!!" ## myS.setLen(3) # myS <- "Nim" ## echo myS, " is fantastic!!" @@ -990,25 +990,25 @@ proc newStringOfCap*(cap: Natural): string {. proc `&`*(x: string, y: char): string {. magic: "ConStrStr", noSideEffect.} ## Concatenates `x` with `y`. - ## ``` + ## ```nim ## assert("ab" & 'c' == "abc") ## ``` proc `&`*(x, y: char): string {. magic: "ConStrStr", noSideEffect.} ## Concatenates characters `x` and `y` into a string. - ## ``` + ## ```nim ## assert('a' & 'b' == "ab") ## ``` proc `&`*(x, y: string): string {. magic: "ConStrStr", noSideEffect.} ## Concatenates strings `x` and `y`. - ## ``` + ## ```nim ## assert("ab" & "cd" == "abcd") ## ``` proc `&`*(x: char, y: string): string {. magic: "ConStrStr", noSideEffect.} ## Concatenates `x` with `y`. - ## ``` + ## ```nim ## assert('a' & "bc" == "abc") ## ``` @@ -1017,7 +1017,7 @@ proc `&`*(x: char, y: string): string {. proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} ## Appends `y` to `x` in place. - ## ``` + ## ```nim ## var tmp = "" ## tmp.add('a') ## tmp.add('b') @@ -1150,7 +1150,7 @@ when defined(nimscript) or not defined(nimSeqsV2): ## containers should also call their adding proc `add` for consistency. ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. - ## ``` + ## ```nim ## var s: seq[string] = @["test2","test2"] ## s.add("test") ## assert s == @["test2", "test2", "test"] @@ -1164,7 +1164,7 @@ when false: # defined(gcDestructors): ## containers should also call their adding proc `add` for consistency. ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. - ## ``` + ## ```nim ## var s: seq[string] = @["test2","test2"] ## s.add("test") # s <- @[test2, test2, test] ## ``` @@ -1230,7 +1230,7 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. - ## ``` + ## ```nim ## var i = @[1, 3, 5] ## i.insert(99, 0) # i <- @[99, 1, 3, 5] ## ``` @@ -1261,7 +1261,7 @@ when not defined(nimV2): ## ## It works even for complex data graphs with cycles. This is a great ## debugging tool. - ## ``` + ## ```nim ## var s: seq[string] = @["test2", "test2"] ## var i = @[1, 2, 3, 4, 5] ## echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"] @@ -1294,7 +1294,7 @@ proc toFloat*(i: int): float {.noSideEffect, inline.} = ## If the conversion fails, `ValueError` is raised. ## However, on most platforms the conversion cannot fail. ## - ## ``` + ## ```nim ## let ## a = 2 ## b = 3.7 @@ -1317,7 +1317,7 @@ proc toInt*(f: float): int {.noSideEffect.} = ## ## Note that some floating point numbers (e.g. infinity or even 1e19) ## cannot be accurately converted. - ## ``` + ## ```nim ## doAssert toInt(0.49) == 0 ## doAssert toInt(0.5) == 1 ## doAssert toInt(-0.5) == -1 # rounding is symmetrical @@ -1330,7 +1330,7 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} = proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## Division of integers that results in a float. - ## ``` + ## ```nim ## echo 7 / 5 # => 1.4 ## ``` ## @@ -1397,7 +1397,7 @@ proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## This is often more efficient than `tmp = a; a = b; b = tmp`. ## Particularly useful for sorting algorithms. ## - ## ``` + ## ```nim ## var ## a = 5 ## b = 9 @@ -1436,7 +1436,7 @@ include "system/iterators_1" proc len*[U: Ordinal; V: Ordinal](x: HSlice[U, V]): int {.noSideEffect, inline.} = ## Length of ordinal slice. When x.b < x.a returns zero length. - ## ``` + ## ```nim ## assert((0..5).len == 6) ## assert((5..2).len == 0) ## ``` @@ -1477,7 +1477,7 @@ when defined(nimSeqsV2): ## Concatenates two sequences. ## ## Requires copying of the sequences. - ## ``` + ## ```nim ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) ## ``` ## @@ -1493,7 +1493,7 @@ when defined(nimSeqsV2): ## Appends element y to the end of the sequence. ## ## Requires copying of the sequence. - ## ``` + ## ```nim ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) ## ``` ## @@ -1508,7 +1508,7 @@ when defined(nimSeqsV2): ## Prepends the element x to the beginning of the sequence. ## ## Requires copying of the sequence. - ## ``` + ## ```nim ## assert(1 & @[2, 3, 4] == @[1, 2, 3, 4]) ## ``` newSeq(result, y.len + 1) @@ -1522,7 +1522,7 @@ else: ## Concatenates two sequences. ## ## Requires copying of the sequences. - ## ``` + ## ```nim ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) ## ``` ## @@ -1538,7 +1538,7 @@ else: ## Appends element y to the end of the sequence. ## ## Requires copying of the sequence. - ## ``` + ## ```nim ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) ## ``` ## @@ -1553,7 +1553,7 @@ else: ## Prepends the element x to the beginning of the sequence. ## ## Requires copying of the sequence. - ## ``` + ## ```nim ## assert(1 & @[2, 3, 4] == @[1, 2, 3, 4]) ## ``` newSeq(result, y.len + 1) @@ -1574,7 +1574,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[ ## to retrieve information about the current filename and line number. ## Example: ## - ## ``` + ## ```nim ## import std/strutils ## ## template testException(exception, code: untyped): typed = @@ -1674,7 +1674,7 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}= ## ## This allows the `in` operator: `a.contains(item)` is the same as ## `item in a`. - ## ``` + ## ```nim ## var a = @[1, 3, 5] ## assert a.contains(5) ## assert 3 in a @@ -1768,7 +1768,7 @@ when notJSnotNims: ## ## `outOfMemHook` can be used to raise an exception in case of OOM like so: ## - ## ``` + ## ```nim ## var gOutOfMem: ref EOutOfMemory ## new(gOutOfMem) # need to be allocated *before* OOM really happened! ## gOutOfMem.msg = "out of memory" @@ -1882,7 +1882,7 @@ template likely*(val: bool): bool = ## You can use this template to decorate a branch condition. On certain ## platforms this can help the processor predict better which branch is ## going to be run. Example: - ## ``` + ## ```nim ## for value in inputValues: ## if likely(value <= 100): ## process(value) @@ -1906,7 +1906,7 @@ template unlikely*(val: bool): bool = ## You can use this proc to decorate a branch condition. On certain ## platforms this can help the processor predict better which branch is ## going to be run. Example: - ## ``` + ## ```nim ## for value in inputValues: ## if unlikely(value > 100): ## echo "Value too big!" @@ -2102,7 +2102,7 @@ when notJSnotNims: ## is pressed. Only one such hook is supported. ## Example: ## - ## ``` + ## ```nim ## proc ctrlc() {.noconv.} = ## echo "Ctrl+C fired!" ## # do clean up stuff @@ -2339,7 +2339,7 @@ include "system/indices" proc `&=`*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} ## Appends in place to a string. - ## ``` + ## ```nim ## var a = "abc" ## a &= "de" # a <- "abcde" ## ``` @@ -2418,7 +2418,7 @@ proc repr*[T, U](x: HSlice[T, U]): string = when hasAlloc or defined(nimscript): proc insert*(x: var string, item: string, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. - ## ``` + ## ```nim ## var a = "abc" ## a.insert("zz", 0) # a <- "zzabc" ## ``` @@ -2497,7 +2497,7 @@ proc addQuoted*[T](s: var string, x: T) = ## Users may overload `addQuoted` for custom (string-like) types if ## they want to implement a customized element representation. ## - ## ``` + ## ```nim ## var tmp = "" ## tmp.addQuoted(1) ## tmp.add(", ") @@ -2539,7 +2539,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## the official signature says, the return type is *not* `RootObj` but a ## tuple of a structure that depends on the current scope. Example: ## - ## ``` + ## ```nim ## proc testLocals() = ## var ## a = "something" @@ -2578,7 +2578,7 @@ when hasAlloc and notJSnotNims: proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} = ## Special magic to prohibit dynamic binding for `method`:idx: calls. ## This is similar to `super`:idx: in ordinary OO languages. - ## ``` + ## ```nim ## # 'someMethod' will be resolved fully statically: ## procCall someMethod(a, b) ## ``` @@ -2603,7 +2603,7 @@ template closureScope*(body: untyped): untyped = ## ## Example: ## - ## ``` + ## ```nim ## var myClosure : proc() ## # without closureScope: ## for i in 0 .. 5: @@ -2623,7 +2623,7 @@ template closureScope*(body: untyped): untyped = template once*(body: untyped): untyped = ## Executes a block of code only once (the first time the block is reached). - ## ``` + ## ```nim ## proc draw(t: Triangle) = ## once: ## graphicsInit() diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim index c36bd98319f2..7cf80eb17070 100644 --- a/lib/system/compilation.nim +++ b/lib/system/compilation.nim @@ -1,7 +1,7 @@ const NimMajor* {.intdefine.}: int = 2 ## is the major number of Nim's version. Example: - ## ``` + ## ```nim ## when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard ## ``` # see also std/private/since @@ -40,7 +40,7 @@ proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## `x` is an external symbol introduced through the compiler's ## `-d:x switch <nimc.html#compiler-usage-compileminustime-symbols>`_ to enable ## build time conditionals: - ## ``` + ## ```nim ## when not defined(release): ## # Do here programmer friendly expensive sanity checks. ## # Put here the normal code @@ -57,7 +57,7 @@ proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime. ## ## This can be used to check whether a library provides a certain ## feature or not: - ## ``` + ## ```nim ## when not declared(strutils.toUpper): ## # provide our own toUpper proc here, because strutils is ## # missing it. @@ -74,7 +74,7 @@ proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime. ## Special compile-time procedure that checks whether `x` can be compiled ## without any semantic error. ## This can be used to check whether a type supports some operation: - ## ``` + ## ```nim ## when compiles(3 + 4): ## echo "'+' for integers is available" ## ``` @@ -164,7 +164,7 @@ proc staticRead*(filename: string): string {.magic: "Slurp".} ## ## The maximum file size limit that `staticRead` and `slurp` can read is ## near or equal to the *free* memory of the device you are using to compile. - ## ``` + ## ```nim ## const myResource = staticRead"mydatafile.bin" ## ``` ## @@ -181,7 +181,7 @@ proc staticExec*(command: string, input = "", cache = ""): string {. ## ## If `input` is not an empty string, it will be passed as a standard input ## to the executed program. - ## ``` + ## ```nim ## const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & ## "\nCompiled on " & staticExec("uname -v") ## ``` @@ -197,7 +197,7 @@ proc staticExec*(command: string, input = "", cache = ""): string {. ## behaviour then. `command & input & cache` (the concatenated string) is ## used to determine whether the entry in the cache is still valid. You can ## use versioning information for `cache`: - ## ``` + ## ```nim ## const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0") ## ``` diff --git a/lib/system/indices.nim b/lib/system/indices.nim index fd6770e2328f..f4a1403464d0 100644 --- a/lib/system/indices.nim +++ b/lib/system/indices.nim @@ -10,7 +10,7 @@ template `^`*(x: int): BackwardsIndex = BackwardsIndex(x) ## Builtin `roof`:idx: operator that can be used for convenient array access. ## `a[^x]` is a shortcut for `a[a.len-x]`. ## - ## ``` + ## ```nim ## let ## a = [1, 3, 5, 7, 9] ## b = "abcdefgh" @@ -46,7 +46,7 @@ template `..^`*(a, b: untyped): untyped = template `..<`*(a, b: untyped): untyped = ## A shortcut for `a .. pred(b)`. - ## ``` + ## ```nim ## for i in 5 ..< 9: ## echo i # => 5; 6; 7; 8 ## ``` @@ -76,7 +76,7 @@ template spliceImpl(s, a, L, b: typed): untyped = proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline, systemRaisesDefect.} = ## Slice operation for strings. ## Returns the inclusive range `[s[x.a], s[x.b]]`: - ## ``` + ## ```nim ## var s = "abcdef" ## assert s[1..3] == "bcd" ## ``` @@ -106,7 +106,7 @@ proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) {.systemRa proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} = ## Slice operation for arrays. ## Returns the inclusive range `[a[x.a], a[x.b]]`: - ## ``` + ## ```nim ## var a = [1, 2, 3, 4] ## assert a[0..2] == @[1, 2, 3] ## ``` @@ -117,7 +117,7 @@ proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.s proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} = ## Slice assignment for arrays. - ## ``` + ## ```nim ## var a = [10, 20, 30, 40, 50] ## a[1..2] = @[99, 88] ## assert a == [10, 99, 88, 40, 50] @@ -132,7 +132,7 @@ proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: ope proc `[]`*[T; U, V: Ordinal](s: openArray[T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} = ## Slice operation for sequences. ## Returns the inclusive range `[s[x.a], s[x.b]]`: - ## ``` + ## ```nim ## var s = @[1, 2, 3, 4] ## assert s[0..2] == @[1, 2, 3] ## ``` From 93407096db4192a77002fad9d974d46878c2186b Mon Sep 17 00:00:00 2001 From: PhilippMDoerner <philippmdoerner@web.de> Date: Sat, 19 Aug 2023 17:25:38 +0200 Subject: [PATCH 193/347] #22514 expand testament option docs (#22516) * #22514 Expand docs on testament spec options The file, line and column options of testament are not in the docs, but can be very important to know. They allow you to specify where a compile-time error originated from. Particularly given that testament assumes the origin to always be the test-file, this is important to know. * #22514 Specify nimout relevance a bit more * #22514 Fix slightly erroneous doc-link * #22514 Add example * #22514 Add some docs on ccodecheck --- doc/testament.md | 61 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/doc/testament.md b/doc/testament.md index 08c3cd8353ae..0ff3591ac985 100644 --- a/doc/testament.md +++ b/doc/testament.md @@ -112,8 +112,21 @@ Example "template" **to edit** and write a Testament unittest: # "run": expect successful compilation and execution # "reject": expect failed compilation. The "reject" action can catch # {.error.} pragmas but not {.fatal.} pragmas because + # {.error.} calls are expected to originate from the test-file, + # and can explicitly be specified using the "file", "line" and + # "column" options. # {.fatal.} pragmas guarantee that compilation will be aborted. action: "run" + + # For testing failed compilations you can specify the expected origin of the + # compilation error. + # With the "file", "line" and "column" options you can define the file, + # line and column that a compilation-error should have originated from. + # Use only with action: "reject" as it expects a failed compilation. + # Requires errormsg or msg to be defined. + # file: "" + # line: "" + # column: "" # The exit code that the test is expected to return. Typically, the default # value of 0 is fine. Note that if the test will be run by valgrind, then @@ -127,13 +140,14 @@ Example "template" **to edit** and write a Testament unittest: output: "" outputsub: "" - # Whether to sort the output lines before comparing them to the desired - # output. + # Whether to sort the compiler output lines before comparing them to the + # expected output. sortoutput: true - # Each line in the string given here appears in the same order in the - # compiler output, but there may be more lines that appear before, after, or - # in between them. + # Provide a `nimout` string to assert that the compiler during compilation + # prints the defined lines to the standard out. + # The lines must match in order, but there may be more lines that appear + # before, after, or in between them. nimout: ''' a very long, multi-line @@ -160,6 +174,9 @@ Example "template" **to edit** and write a Testament unittest: # "leaks": run the test with Valgrind, but do not check for memory leaks valgrind: false # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only). + # Checks that the specified piece of C-code is within the generated C-code. + ccodecheck: "'Assert error message'" + # Command the test should use to run. If left out or an empty string is # provided, the command is taken to be: # "nim $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file" @@ -196,7 +213,7 @@ Example "template" **to edit** and write a Testament unittest: * As you can see the "Spec" is just a `discard """ """`. * Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine. * This is not the full spec of Testament, check [the Testament Spec on GitHub, - see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L315). + see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L317). * Nim itself uses Testament, so [there are plenty of test examples]( https://github.com/nim-lang/Nim/tree/devel/tests). * Has some built-in CI compatibility, like Azure Pipelines, etc. @@ -283,6 +300,38 @@ Expected to fail: assert not_defined == "not_defined", "not_defined is not defined" ``` +Expected to fail with error thrown from another file: +```nim +# test.nim +discard """ + action: "reject" + errorMsg: "I break" + file: "breakPragma.nim" +""" +import ./breakPragma + +proc x() {.justDo.} = discard + +# breakPragma.nim +import std/macros + +macro justDo*(procDef: typed): untyped = + error("I break") + return procDef +``` + +Expecting generated C to contain a given piece of code: + + ```nim + discard """ + # Checks that the string "Assert error message" is in the generated + # C code. + ccodecheck: "'Assert error message'" + """ + assert 42 == 42, "Assert error message" + ``` + + Non-Zero exit code: ```nim From c0ecdb01a967b1b903a756abf7202bfbd95ec7b1 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sat, 19 Aug 2023 21:04:25 +0200 Subject: [PATCH 194/347] Fix #21722 (#22512) * Keep return in mind for sink * Keep track of return using bool instead of mode * Update compiler/injectdestructors.nim * Add back IsReturn --------- Co-authored-by: SirOlaf <> Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/injectdestructors.nim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 829076d4936c..a5ec6c21a691 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -79,11 +79,11 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = proc nestedScope(parent: var Scope; body: PNode): Scope = Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent)) -proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode +proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode type MoveOrCopyFlag = enum - IsDecl, IsExplicitSink + IsDecl, IsExplicitSink, IsReturn proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode @@ -272,7 +272,7 @@ proc deepAliases(dest, ri: PNode): bool = 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): + isNoInit(dest) or IsReturn in flags: # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) else: @@ -765,7 +765,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = result.add copyNode(n[0]) s.needsTry = true -proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode = +proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode = if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}: template process(child, s): untyped = p(child, c, s, mode) @@ -949,7 +949,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}: cycleCheck(n, c) assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn} - let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {} + var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {} + if inReturn: + flags.incl(IsReturn) result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags) elif isDiscriminantField(n[0]): result = c.genDiscriminantAsgn(s, n) @@ -1033,7 +1035,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing of nkReturnStmt: result = shallowCopy(n) for i in 0..<n.len: - result[i] = p(n[i], c, s, mode) + result[i] = p(n[i], c, s, mode, inReturn=true) s.needsTry = true of nkCast: result = shallowCopy(n) From a4781dc4bcdf6e76076af80d0b21cb09865b3d44 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Sun, 20 Aug 2023 07:30:36 +0300 Subject: [PATCH 195/347] use old typeinfo generation for hot code reloading (#22518) * use old typeinfo generation for hot code reloading * at least test hello world compilation on orc --- compiler/ccgtypes.nim | 2 +- testament/categories.nim | 7 +++++-- tests/dll/nimhcr_basic.nim | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index eb5e906e425f..b7363f67fe87 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1772,7 +1772,7 @@ proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope = return prefixTI.rope & result & ")".rope m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner) - if m.compileToCpp: + if m.compileToCpp or m.hcrOn: genTypeInfoV2OldImpl(m, t, origType, result, info) else: genTypeInfoV2Impl(m, t, origType, result, info) diff --git a/testament/categories.nim b/testament/categories.nim index c449cf3e04e0..843bef3f9ad5 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -81,10 +81,13 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string, isOrc = testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & " --threads:off" & rpath, cat) testSpec r, makeTest("tests/dll/visibility.nim", options & " --threads:off" & rpath, cat) - if "boehm" notin options and not isOrc: + if "boehm" notin options: # hcr tests - testSpec r, makeTest("tests/dll/nimhcr_basic.nim", options & " --threads:off --forceBuild --hotCodeReloading:on " & rpath, cat) + var basicHcrTest = makeTest("tests/dll/nimhcr_basic.nim", options & " --threads:off --forceBuild --hotCodeReloading:on " & rpath, cat) + # test segfaults for now but compiles: + if isOrc: basicHcrTest.spec.action = actionCompile + testSpec r, basicHcrTest # force build required - see the comments in the .nim file for more details var hcri = makeTest("tests/dll/nimhcr_integration.nim", diff --git a/tests/dll/nimhcr_basic.nim b/tests/dll/nimhcr_basic.nim index 340c3fc4e958..2e1f39ae0647 100644 --- a/tests/dll/nimhcr_basic.nim +++ b/tests/dll/nimhcr_basic.nim @@ -3,5 +3,6 @@ discard """ Hello world ''' """ +# for now orc only tests successful compilation echo "Hello world" From 942f846f04b4fa3c3154f7277ab1a8f6762d2714 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Mon, 21 Aug 2023 21:08:00 +0300 Subject: [PATCH 196/347] fix getNullValue for cstring in VM, make other VM code aware of nil cstring (#22527) * fix getNullValue for cstring in VM fixes #22524 * very ugly fixes, but fix #15730 * nil cstring len works, more test lines * fix high --- compiler/vm.nim | 19 +++++++++++++++++-- compiler/vmdef.nim | 2 +- compiler/vmgen.nim | 7 ++++--- tests/vm/tvmmisc.nim | 31 +++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 1f4e4333d0f3..79832dbcb524 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1017,7 +1017,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLenCstring: decodeBImm(rkInt) assert regs[rb].kind == rkNode - regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm + if regs[rb].node.kind == nkNilLit: + regs[ra].intVal = -imm + else: + regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm of opcIncl: decodeB(rkNode) let b = regs[rb].regToNode @@ -1215,6 +1218,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcEqStr: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal) + of opcEqCString: + decodeBC(rkInt) + let bNil = regs[rb].node.kind == nkNilLit + let cNil = regs[rc].node.kind == nkNilLit + regs[ra].intVal = ord((bNil and cNil) or + (not bNil and not cNil and regs[rb].node.strVal == regs[rc].node.strVal)) of opcLeStr: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal) @@ -1498,7 +1507,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node c.currentExceptionA = raised # Set the `name` field of the exception - c.currentExceptionA[2].skipColon.strVal = c.currentExceptionA.typ.sym.name.s + var exceptionNameNode = newStrNode(nkStrLit, c.currentExceptionA.typ.sym.name.s) + if c.currentExceptionA[2].kind == nkExprColonExpr: + exceptionNameNode.typ = c.currentExceptionA[2][1].typ + c.currentExceptionA[2][1] = exceptionNameNode + else: + exceptionNameNode.typ = c.currentExceptionA[2].typ + c.currentExceptionA[2] = exceptionNameNode c.exceptionInstr = pc var frame = tos diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index f369908ba4ff..bdb0aeed1551 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -99,7 +99,7 @@ type opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimNode, opcSameNodeType, opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, - opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, + opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcIsNil, opcOf, opcIs, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 8aaac6272c9f..e58ddbeb91d3 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1159,7 +1159,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcNarrowU, dest, TRegister(size*8)) of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n[1], dest) - of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) + of mEqStr: genBinaryABC(c, n, dest, opcEqStr) + of mEqCString: genBinaryABC(c, n, dest, opcEqCString) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) of mEqSet: genBinarySet(c, n, dest, opcEqSet) @@ -1877,10 +1878,10 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyCstring, tyString: + of tyString: result = newNodeIT(nkStrLit, info, t) result.strVal = "" - of tyVar, tyLent, tyPointer, tyPtr, tyUntyped, + of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, tyTyped, tyTypeDesc, tyRef, tyNil: result = newNodeIT(nkNilLit, info, t) of tyProc: diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index da8208f73e92..5760422a1bd0 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -735,3 +735,34 @@ block: # bug #22190 tab = mkOpTable(Berlin) doAssert not tab + +block: # issue #22524 + const cnst = cstring(nil) + doAssert cnst.isNil + doAssert cnst == nil + let b = cnst + doAssert b.isNil + doAssert b == nil + + let a = static: cstring(nil) + doAssert a.isNil + + static: + var x: cstring + doAssert x.isNil + doAssert x == nil + doAssert x != "" + +block: # issue #15730 + const s: cstring = "" + doAssert s != nil + + static: + let s: cstring = "" + doAssert not s.isNil + doAssert s != nil + doAssert s == "" + +static: # more nil cstring issues + let x = cstring(nil) + doAssert x.len == 0 From 602f537eb2445b442b6cddecf9f55bf476068ea9 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Mon, 21 Aug 2023 21:08:57 +0300 Subject: [PATCH 197/347] allow non-pragma special words as user pragmas (#22526) allow non-pragma special words as macro pragmas fixes #22525 --- compiler/trees.nim | 8 +++++--- compiler/wordrecg.nim | 27 +++++++++++++++++++-------- tests/pragmas/tpragmas_misc.nim | 5 +++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/compiler/trees.nim b/compiler/trees.nim index c4ddc8cf7c43..cc2b0eafddf8 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -144,10 +144,12 @@ proc whichPragma*(n: PNode): TSpecialWord = case key.kind of nkIdent: result = whichKeyword(key.ident) of nkSym: result = whichKeyword(key.sym.name) - of nkCast: result = wCast + of nkCast: return wCast of nkClosedSymChoice, nkOpenSymChoice: - result = whichPragma(key[0]) - else: result = wInvalid + return whichPragma(key[0]) + else: return wInvalid + if result in nonPragmaWordsLow..nonPragmaWordsHigh: + result = wInvalid proc isNoSideEffectPragma*(n: PNode): bool = var k = whichPragma(n) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index b2b0c8ae2389..1724b18f6ad7 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -91,28 +91,36 @@ type wRedefine = "redefine", wCallsite = "callsite", wQuirky = "quirky", + # codegen keywords, but first the ones that are also pragmas: + wExtern = "extern", wGoto = "goto", wRegister = "register", + wUnion = "union", wPacked = "packed", wVirtual = "virtual", + wVolatile = "volatile", wMember = "member", + wByCopy = "bycopy", wByRef = "byref", + + # codegen keywords but not pragmas: wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast", - wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float", - wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", + wExplicit = "explicit", wFalse = "false", wFloat = "float", + wFriend = "friend", wInt = "int", wLong = "long", wMutable = "mutable", wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", - wProtected = "protected", wPublic = "public", wRegister = "register", + wProtected = "protected", wPublic = "public", wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct", wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", - wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", - wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", wMember = "member", + wUnsigned = "unsigned", wVoid = "void", wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", wThreadLocal = "thread_local", wStaticAssert = "static_assert", - wChar16 = "char16_t", wChar32 = "char32_t", + wChar16 = "char16_t", wChar32 = "char32_t", wWchar = "wchar_t", wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", - wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", + wInOut = "inout", wOneWay = "oneway", + # end of codegen keywords + wBitsize = "bitsize", wImportHidden = "all", wSendable = "sendable" @@ -125,12 +133,15 @@ const nimKeywordsLow* = ord(wAsm) nimKeywordsHigh* = ord(wYield) - ccgKeywordsLow* = ord(wAuto) + ccgKeywordsLow* = ord(wExtern) ccgKeywordsHigh* = ord(wOneWay) cppNimSharedKeywords* = { wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport, wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} + + nonPragmaWordsLow* = wAuto + nonPragmaWordsHigh* = wOneWay from std/enumutils import genEnumCaseStmt diff --git a/tests/pragmas/tpragmas_misc.nim b/tests/pragmas/tpragmas_misc.nim index 6dc2e6b8022c..adb7e73c34b6 100644 --- a/tests/pragmas/tpragmas_misc.nim +++ b/tests/pragmas/tpragmas_misc.nim @@ -68,3 +68,8 @@ block: # issue #10994 proc a {.bar.} = discard # works proc b {.bar, foo.} = discard # doesn't + +block: # issue #22525 + macro catch(x: typed) = x + proc thing {.catch.} = discard + thing() From a26ccb34768fdaceff2cf4d27aee4c830fd47303 Mon Sep 17 00:00:00 2001 From: Hamid Bluri <hr.bolouri@gmail.com> Date: Tue, 22 Aug 2023 20:01:21 +0330 Subject: [PATCH 198/347] fix #22492 (#22511) * fix #22492 * Update nimdoc.css remove scroll-y * Update nimdoc.out.css * Update nimdoc.css * make it sticky again * Update nimdoc.out.css * danm sticky, use fixed * Update nimdoc.out.css * fix margin * Update nimdoc.out.css * make search input react to any change (not just keyboard events) according to https://github.com/nim-lang/Nim/pull/22511#issuecomment-1685218787 --- config/nimdoc.cfg | 4 ++-- doc/nimdoc.css | 11 ++++++----- nimdoc/extlinks/project/expected/_._/util.html | 2 +- nimdoc/extlinks/project/expected/main.html | 2 +- nimdoc/extlinks/project/expected/sub/submodule.html | 2 +- nimdoc/rst2html/expected/rst_examples.html | 2 +- nimdoc/test_doctype/expected/test_doctype.html | 2 +- nimdoc/test_out_index_dot_html/expected/index.html | 2 +- nimdoc/testproject/expected/nimdoc.out.css | 11 ++++++----- .../testproject/expected/subdir/subdir_b/utils.html | 2 +- nimdoc/testproject/expected/testproject.html | 2 +- 11 files changed, 22 insertions(+), 20 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 9f36e7d1c2ac..9535aa384fc2 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -155,7 +155,7 @@ doc.body_toc_group = """ </div> <div id="searchInputDiv"> Search: <input type="search" id="searchInput" - onkeyup="search()" /> + oninput="search()" /> </div> $body_toc_groupsection $tableofcontents @@ -189,7 +189,7 @@ doc.body_toc_group = """ </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 3fb5497ff62c..a9e4ac9c6aea 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -767,15 +767,16 @@ div.topic { div.search_results { background-color: var(--third-background); - margin: 3em; + margin: 3vh 5vw; padding: 1em; border: 1px solid #4d4d4d; - position: sticky; - top: 0; + position: fixed; + top: 10px; isolation: isolate; + max-width: calc(100vw - 6em); z-index: 1; - max-height: 100vh; - overflow-y: scroll; } + max-height: calc(100vh - 6em); + overflow-y: scroll;} div#global-links ul { margin-left: 0; diff --git a/nimdoc/extlinks/project/expected/_._/util.html b/nimdoc/extlinks/project/expected/_._/util.html index 9b9b29a4f9b3..35f333211289 100644 --- a/nimdoc/extlinks/project/expected/_._/util.html +++ b/nimdoc/extlinks/project/expected/_._/util.html @@ -37,7 +37,7 @@ <h1 class="title">nimdoc/extlinks/util</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/extlinks/project/expected/main.html b/nimdoc/extlinks/project/expected/main.html index cf7982fde191..1a58ea2ac7b0 100644 --- a/nimdoc/extlinks/project/expected/main.html +++ b/nimdoc/extlinks/project/expected/main.html @@ -37,7 +37,7 @@ <h1 class="title">nimdoc/extlinks/project/main</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/extlinks/project/expected/sub/submodule.html b/nimdoc/extlinks/project/expected/sub/submodule.html index 913138d6ec2b..60887ae37ad3 100644 --- a/nimdoc/extlinks/project/expected/sub/submodule.html +++ b/nimdoc/extlinks/project/expected/sub/submodule.html @@ -37,7 +37,7 @@ <h1 class="title">nimdoc/extlinks/project/sub/submodule</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 72f6453f25a0..af46771f37e6 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -37,7 +37,7 @@ <h1 class="title">Not a Nim Manual</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/test_doctype/expected/test_doctype.html b/nimdoc/test_doctype/expected/test_doctype.html index 01ad6e5a09a8..694ed9b00e4a 100644 --- a/nimdoc/test_doctype/expected/test_doctype.html +++ b/nimdoc/test_doctype/expected/test_doctype.html @@ -37,7 +37,7 @@ <h1 class="title">nimdoc/test_doctype/test_doctype</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 22d5daa35863..40df1943a84c 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -37,7 +37,7 @@ <h1 class="title">nimdoc/test_out_index_dot_html/foo</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 3fb5497ff62c..a9e4ac9c6aea 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -767,15 +767,16 @@ div.topic { div.search_results { background-color: var(--third-background); - margin: 3em; + margin: 3vh 5vw; padding: 1em; border: 1px solid #4d4d4d; - position: sticky; - top: 0; + position: fixed; + top: 10px; isolation: isolate; + max-width: calc(100vw - 6em); z-index: 1; - max-height: 100vh; - overflow-y: scroll; } + max-height: calc(100vh - 6em); + overflow-y: scroll;} div#global-links ul { margin-left: 0; diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index cfdac5310781..ba9512d5a9d7 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -37,7 +37,7 @@ <h1 class="title">subdir/subdir_b/utils</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 78a730e59844..db49102f85aa 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -37,7 +37,7 @@ <h1 class="title">testproject</h1> </ul> </div> <div id="searchInputDiv"> - Search: <input type="search" id="searchInput" onkeyup="search()"/> + Search: <input type="search" id="searchInput" oninput="search()"/> </div> <div> Group by: From 6b04d0395ab1d6d8efa7d287c73da2c7230800c9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf <rumpf_a@web.de> Date: Tue, 22 Aug 2023 21:01:08 +0200 Subject: [PATCH 199/347] allow tuples and procs in 'toTask' + minor things (#22530) --- lib/pure/strscans.nim | 4 ++-- lib/std/tasks.nim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 775c4244ac11..0b23abc28bf5 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -172,7 +172,7 @@ The parsing is performed with the help of 3 helper templates that that can be implemented for a custom type. These templates need to be named ``atom`` and ``nxt``. ``atom`` should be -overloaded to handle both single characters and sets of character. +overloaded to handle both `char` and `set[char]`. ```nim import std/streams @@ -472,7 +472,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[untyped]): untyped {.since: (1, 5).}= ## Works identically as scanf, but instead of predeclaring variables it returns a tuple. - ## Tuple is started with a bool which indicates if the scan was successful + ## Tuple is started with a bool which indicates if the scan was successful ## followed by the requested data. ## If using a user defined matcher, provide the types in order they appear after pattern: ## `line.scanTuple("${yourMatcher()}", int)` diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 6a4b2bb6d8cd..9eb7c97c4ee7 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -173,7 +173,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC # passing by static parameters # so we pass them directly instead of passing by scratchObj callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) - of nnkSym, nnkPtrTy: + of nnkSym, nnkPtrTy, nnkProcTy, nnkTupleConstr: addAllNode(param, e[i]) of nnkCharLit..nnkNilLit: callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) From 3de75ffc02ea85b95702ec0dc0976748954d3e2c Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Wed, 23 Aug 2023 06:18:35 +0200 Subject: [PATCH 200/347] Fix #21532: Check if template return is untyped (#22517) * Don't ignore return in semTemplateDef * Add test --------- Co-authored-by: SirOlaf <> --- compiler/semtempl.nim | 3 +++ tests/template/t21532.nim | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/template/t21532.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a72143bdd7bb..85411f7c4696 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -674,6 +674,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: + let retType = s.typ[0] + if retType != nil and retType.kind != tyUntyped: + allUntyped = false for i in 1..<s.typ.n.len: let param = s.typ.n[i].sym if param.name.id != ord(wUnderscore): diff --git a/tests/template/t21532.nim b/tests/template/t21532.nim new file mode 100644 index 000000000000..3193b0dc3559 --- /dev/null +++ b/tests/template/t21532.nim @@ -0,0 +1,8 @@ + +template elementType(a: untyped): typedesc = + typeof(block: (for ai in a: ai)) + +func fn[T](a: T) = + doAssert elementType(a) is int + +@[1,2,3].fn \ No newline at end of file From 4f891aa50c298bcb81fc8859e2118b3118188720 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Wed, 23 Aug 2023 14:43:02 +0300 Subject: [PATCH 201/347] don't render underscore identifiers with id (#22538) --- compiler/renderer.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index e39be78fe964..c73fa994510d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -14,7 +14,7 @@ {.used.} import - lexer, options, idents, strutils, ast, msgs, lineinfos + lexer, options, idents, strutils, ast, msgs, lineinfos, wordrecg when defined(nimPreviewSlimSystem): import std/[syncio, assertions, formatfloat] @@ -838,7 +838,7 @@ proc gcase(g: var TSrcGen, n: PNode) = gsub(g, n[^1], c) proc genSymSuffix(result: var string, s: PSym) {.inline.} = - if sfGenSym in s.flags: + if sfGenSym in s.flags and s.name.id != ord(wUnderscore): result.add '_' result.addInt s.id @@ -958,7 +958,9 @@ proc gident(g: var TSrcGen, n: PNode) = s.addInt localId if sfCursor in n.sym.flags: s.add "_cursor" - elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp): + elif n.kind == nkSym and (renderIds in g.flags or + (sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or + n.sym.kind == skTemp): s.add '_' s.addInt n.sym.id when defined(debugMagics): From 03f267c8013eca2830eb3deadda73ed08096ec12 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Wed, 23 Aug 2023 20:25:26 +0300 Subject: [PATCH 202/347] make jsffi properly gensym (#22539) fixes #21208 --- lib/js/jsffi.nim | 51 ++++++++++++++++++++++++++------------------- tests/js/tjsffi.nim | 6 ++++++ 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index db40e75150b7..08b1c6db9ebb 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -227,36 +227,40 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = assert obj.a.to(int) == 20 if validJsName($field): let importString = "#." & $field + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym.} - helper(`obj`) + proc `helperName`(o: JsObject): JsObject + {.importjs: `importString`.} + `helperName`(`obj`) else: if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym.} - helper(`obj`) + proc `helperName`(o: JsObject): JsObject + {.importjs: `importString`.} + `helperName`(`obj`) macro `.=`*(obj: JsObject, field, value: untyped): untyped = ## Experimental dot accessor (set) for type JsObject. ## Sets the value of a property of name `field` in a JsObject `x` to `value`. if validJsName($field): let importString = "#." & $field & " = #" + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject, v: auto) - {.importjs: `importString`, gensym.} - helper(`obj`, `value`) + proc `helperName`(o: JsObject, v: auto) + {.importjs: `importString`.} + `helperName`(`obj`, `value`) else: if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] & " = #" + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: JsObject, v: auto) - {.importjs: `importString`, gensym.} - helper(`obj`, `value`) + proc `helperName`(o: JsObject, v: auto) + {.importjs: `importString`.} + `helperName`(`obj`, `value`) macro `.()`*(obj: JsObject, field: untyped, @@ -283,10 +287,11 @@ macro `.()`*(obj: JsObject, if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & "(@)" - result = quote: - proc helper(o: JsObject): JsObject - {.importjs: `importString`, gensym, discardable.} - helper(`obj`) + let helperName = genSym(nskProc, "helper") + result = quote do: + proc `helperName`(o: JsObject): JsObject + {.importjs: `importString`, discardable.} + `helperName`(`obj`) for idx in 0 ..< args.len: let paramName = newIdentNode("param" & $idx) result[0][3].add newIdentDefs(paramName, newIdentNode("JsObject")) @@ -303,10 +308,11 @@ macro `.`*[K: cstring, V](obj: JsAssoc[K, V], if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: type(`obj`)): `obj`.V - {.importjs: `importString`, gensym.} - helper(`obj`) + proc `helperName`(o: type(`obj`)): `obj`.V + {.importjs: `importString`.} + `helperName`(`obj`) macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], field: untyped, @@ -320,10 +326,11 @@ macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], if not mangledNames.hasKey($field): mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & " = #" + let helperName = genSym(nskProc, "helper") result = quote do: - proc helper(o: type(`obj`), v: `obj`.V) - {.importjs: `importString`, gensym.} - helper(`obj`, `value`) + proc `helperName`(o: type(`obj`), v: `obj`.V) + {.importjs: `importString`.} + `helperName`(`obj`, `value`) macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], field: untyped, diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 2e57f70c1ef3..b54d13e43e75 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -265,3 +265,9 @@ block: # test ** doAssert to(`**`(a + a, b), int) == 2 doAssert to(`**`(toJs(1) + toJs(1), toJs(2)), int) == 4 + +block: # issue #21208 + type MyEnum = enum baz + var obj: JsObject + {.emit: "`obj` = {bar: {baz: 123}}".} + discard obj.bar.baz From 53d43e96716539d96e6a1e5f3926a3fe3a11e2dd Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Thu, 24 Aug 2023 07:11:48 +0300 Subject: [PATCH 203/347] round out tuple unpacking assignment, support underscores (#22537) * round out tuple unpacking assignment, support underscores fixes #18710 * fix test messages * use discard instead of continue Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/lowerings.nim | 19 ------------------ compiler/semexprs.nim | 33 ++++++++++++++++++++++++++++++- compiler/semstmts.nim | 17 +++++++++------- tests/arc/topt_no_cursor.nim | 8 ++++---- tests/errmsgs/tassignunpack.nim | 2 +- tests/tuples/ttuples_various.nim | 12 +++++++++++ tests/types/tassignemptytuple.nim | 2 +- 7 files changed, 60 insertions(+), 33 deletions(-) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index a083b9195315..42d0f1790c0d 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -122,25 +122,6 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode = proc newTryFinally*(body, final: PNode): PNode = result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final)) -proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = - let value = n.lastSon - result = newNodeI(nkStmtList, n.info) - - var temp = newSym(skTemp, getIdent(g.cache, "_"), idgen, owner, value.info, owner.options) - var v = newNodeI(nkLetSection, value.info) - let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) - - var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) - vpart[0] = tempAsNode - vpart[1] = newNodeI(nkTupleClassTy, value.info) - vpart[2] = value - v.add vpart - result.add(v) - - let lhs = n[0] - for i in 0..<lhs.len: - result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i)) - proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = result = newNodeI(nkStmtList, n.info) # note: cannot use 'skTemp' here cause we really need the copy for the VM :-( diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 52d1f0628ea7..cb27ca0ff448 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1828,6 +1828,37 @@ proc goodLineInfo(arg: PNode): TLineInfo = else: arg.info +proc makeTupleAssignments(c: PContext; n: PNode): PNode = + ## expand tuple unpacking assignment into series of assignments + ## + ## mirrored with semstmts.makeVarTupleSection + let lhs = n[0] + let value = semExprWithType(c, n[1], {efTypeAllowed}) + if value.typ.kind != tyTuple: + localError(c.config, n[1].info, errXExpected, "tuple") + elif lhs.len != value.typ.len: + localError(c.config, n.info, errWrongNumberOfVariables) + result = newNodeI(nkStmtList, n.info) + + let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info) + temp.typ = value.typ + temp.flags.incl(sfGenSym) + var v = newNodeI(nkLetSection, value.info) + let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) + var vpart = newNodeI(nkIdentDefs, v.info, 3) + vpart[0] = tempNode + vpart[1] = c.graph.emptyNode + vpart[2] = value + v.add vpart + result.add(v) + + for i in 0..<lhs.len: + if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore): + # skip _ assignments if we are using a temp as they are already evaluated + discard + else: + result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i)) + proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = checkSonsLen(n, 2, c.config) var a = n[0] @@ -1870,7 +1901,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = # 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: - return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {}) + return semStmt(c, makeTupleAssignments(c, n), {}) else: a = semExprWithType(c, a, {efLValue}) else: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 39c37f4fb25f..bb9c474a8f85 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -599,12 +599,14 @@ proc globalVarInitCheck(c: PContext, n: PNode) = 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 + ## + ## mirrored with semexprs.makeTupleAssignments 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 = nil + tempNode: PNode = nil lastDef: PNode let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs # temporary not needed if not const and RHS is tuple literal @@ -612,17 +614,18 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy 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"), c.idgen, getCurrOwner(c), n.info) - tmpTuple.typ = typ - tmpTuple.flags.incl(sfGenSym) + let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info) + temp.typ = typ + temp.flags.incl(sfGenSym) lastDef = newNodeI(defkind, a.info) newSons(lastDef, 3) - lastDef[0] = newSymNode(tmpTuple) + lastDef[0] = newSymNode(temp) # NOTE: at the moment this is always ast.emptyNode, see parser.nim lastDef[1] = a[^2] lastDef[2] = def - tmpTuple.ast = lastDef + temp.ast = lastDef addToVarSection(c, origResult, n, lastDef) + tempNode = newSymNode(temp) result = newNodeI(n.kind, a.info) for j in 0..<a.len-2: let name = a[j] @@ -641,7 +644,7 @@ proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSy lastDef[0] = name lastDef[^2] = c.graph.emptyNode if useTemp: - lastDef[^1] = newTreeIT(nkBracketExpr, name.info, typ[j], newSymNode(tmpTuple), newIntNode(nkIntLit, j)) + lastDef[^1] = newTupleAccessRaw(tempNode, j) else: var val = def[j] if val.kind == nkExprColonExpr: val = val[1] diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 7cfb0a0d50bd..dfb0f0a3867a 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -39,13 +39,13 @@ var lresult lvalue lnext - _ + tmpTupleAsgn lresult = @[123] -_ = ( +tmpTupleAsgn = ( let blitTmp = lresult blitTmp, ";") -lvalue = _[0] -lnext = _[1] +lvalue = tmpTupleAsgn[0] +lnext = tmpTupleAsgn[1] `=sink`(result.value, move lvalue) `=destroy`(lnext) `=destroy_1`(lvalue) diff --git a/tests/errmsgs/tassignunpack.nim b/tests/errmsgs/tassignunpack.nim index 27413a42b697..d74e16dd5e9f 100644 --- a/tests/errmsgs/tassignunpack.nim +++ b/tests/errmsgs/tassignunpack.nim @@ -1,3 +1,3 @@ var a, b = 0 (a, b) = 1 #[tt.Error - ^ type mismatch: got <int literal(1)> but expected 'tuple']# + ^ 'tuple' expected]# diff --git a/tests/tuples/ttuples_various.nim b/tests/tuples/ttuples_various.nim index 97bc70bd286d..e392731d2f0a 100644 --- a/tests/tuples/ttuples_various.nim +++ b/tests/tuples/ttuples_various.nim @@ -197,3 +197,15 @@ block: # bug #22054 var v = A(field: (a: 1314)) doAssert get(v)[0] == 1314 + +block: # tuple unpacking assignment with underscore + var + a = 1 + b = 2 + doAssert (a, b) == (1, 2) + (a, _) = (3, 4) + doAssert (a, b) == (3, 2) + (_, a) = (5, 6) + doAssert (a, b) == (6, 2) + (b, _) = (7, 8) + doAssert (a, b) == (6, 7) diff --git a/tests/types/tassignemptytuple.nim b/tests/types/tassignemptytuple.nim index f3320dec7ace..9d5a311baa32 100644 --- a/tests/types/tassignemptytuple.nim +++ b/tests/types/tassignemptytuple.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid type: 'empty' in this context: '(seq[empty], (seq[empty], set[empty]))' for let" + errormsg: "cannot infer the type of the tuple" file: "tassignemptytuple.nim" line: 11 """ From c56a712e7d54485b97df3b110ef148f5c12f2ab3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 24 Aug 2023 18:59:45 +0800 Subject: [PATCH 204/347] fixes #22541; peg matchLen can raise an unlisted exception: Exception (#22545) The `mopProc` is a recursive function. --- lib/pure/pegs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 7f0f532fe5e5..18e26027f589 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -562,7 +562,7 @@ template matchOrParse(mopProc: untyped) = # procs. For the former, *enter* and *leave* event handler code generators # are provided which just return *discard*. - proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe.} = + proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe, raises: [].} = proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int = # Parse handler code must run in an *of* clause of its own for each # *PegKind*, so we encapsulate the identical clause body for From bc9785c08d53c2f94f62738e541508592c9b3b24 Mon Sep 17 00:00:00 2001 From: Jacek Sieka <arnetheduck@gmail.com> Date: Thu, 24 Aug 2023 15:41:29 +0200 Subject: [PATCH 205/347] Fix `getAppFilename` exception handling (#22544) * Fix `getAppFilename` exception handling avoid platform-dependendent error handling strategies * more fixes * space --- lib/pure/os.nim | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 77dc3ca8f865..13b103b92598 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -8,7 +8,7 @@ # ## This module contains basic operating system facilities like -## retrieving environment variables, working with directories, +## retrieving environment variables, working with directories, ## running shell commands, etc. ## .. importdoc:: symlinks.nim, appdirs.nim, dirs.nim, ospaths2.nim @@ -624,10 +624,12 @@ when defined(haiku): else: result = "" -proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} = +proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget, raises: [].} = ## Returns the filename of the application's executable. ## This proc will resolve symlinks. ## + ## Returns empty string when name is unavailable + ## ## See also: ## * `getAppDir proc`_ ## * `getCurrentCompilerExe proc`_ @@ -657,14 +659,17 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW if getExecPath2(result.cstring, size): result = "" # error! if result.len > 0: - result = result.expandFilename + try: + result = result.expandFilename + except OSError: + result = "" else: when defined(linux) or defined(aix): result = getApplAux("/proc/self/exe") elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") elif defined(genode): - raiseOSError(OSErrorCode(-1), "POSIX command line not supported") + result = "" # Not supported elif defined(freebsd) or defined(dragonfly) or defined(netbsd): result = getApplFreebsd() elif defined(haiku): @@ -676,7 +681,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW # little heuristic that may work on other POSIX-like systems: if result.len == 0: - result = getApplHeuristic() + result = try: getApplHeuristic() except OSError: "" proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} = ## Returns the directory of the application's executable. From 101337885459decedc9dc3301b1ff04cf8c54c32 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 25 Aug 2023 02:56:58 +0800 Subject: [PATCH 206/347] fixes a strictdef ten years long vintage bug, which counts the same thing twice (#22549) fixes a strictdef ten years long vintage bug --- compiler/ccgcalls.nim | 1 + compiler/semexprs.nim | 1 + compiler/sempass2.nim | 31 +++++++++++++++++++++---------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 80b534cee6c6..494f3c8c698e 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -24,6 +24,7 @@ proc canRaiseDisp(p: BProc; n: PNode): bool = proc preventNrvo(p: BProc; dest, le, ri: PNode): bool = proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool = + result = false var n = le while true: # do NOT follow nkHiddenDeref here! diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cb27ca0ff448..cb4bddb5d1f8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2150,6 +2150,7 @@ proc expectString(c: PContext, n: PNode): string = if n.kind in nkStrKinds: return n.strVal else: + result = "" localError(c.config, n.info, errStringLiteralExpected) proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 854247095e8e..30050f25d2a0 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -356,12 +356,15 @@ proc useVar(a: PEffects, n: PNode) = type TIntersection = seq[tuple[id, count: int]] # a simple count table -proc addToIntersection(inter: var TIntersection, s: int) = +proc addToIntersection(inter: var TIntersection, s: int, zeroInit: bool) = for j in 0..<inter.len: if s == inter[j].id: inc inter[j].count return - inter.add((id: s, count: 1)) + if zeroInit: + inter.add((id: s, count: 0)) + else: + inter.add((id: s, count: 1)) proc throws(tracked, n, orig: PNode) = if n.typ == nil or n.typ.kind != tyError: @@ -465,7 +468,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = track(tracked, n[0]) dec tracked.inTryStmt for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i]) + addToIntersection(inter, tracked.init[i], false) var branches = 1 var hasFinally = false @@ -500,7 +503,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = tracked.init.add b[j][2].sym.id track(tracked, b[^1]) for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i]) + addToIntersection(inter, tracked.init[i], false) else: setLen(tracked.init, oldState) track(tracked, b[^1]) @@ -698,9 +701,11 @@ proc trackCase(tracked: PEffects, n: PNode) = addCaseBranchFacts(tracked.guards, n, i) for i in 0..<branch.len: track(tracked, branch[i]) - if not breaksBlock(branch.lastSon): inc toCover + let hasBreaksBlock = breaksBlock(branch.lastSon) + if not hasBreaksBlock: + inc toCover for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i]) + addToIntersection(inter, tracked.init[i], hasBreaksBlock) setLen(tracked.init, oldState) if not stringCase or lastSon(n).kind == nkElse: @@ -720,9 +725,11 @@ proc trackIf(tracked: PEffects, n: PNode) = var inter: TIntersection = @[] var toCover = 0 track(tracked, n[0][1]) - if not breaksBlock(n[0][1]): inc toCover + let hasBreaksBlock = breaksBlock(n[0][1]) + if not hasBreaksBlock: + inc toCover for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i]) + addToIntersection(inter, tracked.init[i], hasBreaksBlock) for i in 1..<n.len: let branch = n[i] @@ -734,9 +741,12 @@ proc trackIf(tracked: PEffects, n: PNode) = setLen(tracked.init, oldState) for i in 0..<branch.len: track(tracked, branch[i]) - if not breaksBlock(branch.lastSon): inc toCover + let hasBreaksBlock = breaksBlock(branch.lastSon) + if not hasBreaksBlock: + inc toCover for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i]) + addToIntersection(inter, tracked.init[i], hasBreaksBlock) + setLen(tracked.init, oldState) if lastSon(n).len == 1: for id, count in items(inter): @@ -1327,6 +1337,7 @@ proc track(tracked: PEffects, n: PNode) = proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool = if spec.typ.kind == tyOr: + result = false for t in spec.typ: if safeInheritanceDiff(g.excType(real), t) <= 0: return true From fc6a388780c9a0e2eb6c5eff4e291e7bcfcd5f7a Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Thu, 24 Aug 2023 19:57:49 +0100 Subject: [PATCH 207/347] Add `cursor` to lists iterator variables (#22531) * 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 6e3ddf39752b..e1d32e73728f 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 d677ed31e50a322851b476f20fd1719eb17cd426 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 25 Aug 2023 12:48:08 +0800 Subject: [PATCH 208/347] follow up #22549 (#22551) --- compiler/int128.nim | 2 +- compiler/reorder.nim | 1 + compiler/sempass2.nim | 7 ++++--- compiler/types.nim | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/int128.nim b/compiler/int128.nim index 6968b1f8922f..18e751f98964 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -573,4 +573,4 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} = of 8: return maskUInt64(arg) else: - assert(false, "masking only implemented for 1, 2, 4 and 8 bytes") + raiseAssert "masking only implemented for 1, 2, 4 and 8 bytes" diff --git a/compiler/reorder.nim b/compiler/reorder.nim index a96841bcad66..aedebc7d420a 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -252,6 +252,7 @@ proc hasCommand(n: PNode): bool = of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt, nkLetSection, nkConstSection, nkVarSection, nkIdentDefs: + result = false for a in n: if a.hasCommand: return true diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 30050f25d2a0..0954f42fd01f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -356,12 +356,13 @@ proc useVar(a: PEffects, n: PNode) = type TIntersection = seq[tuple[id, count: int]] # a simple count table -proc addToIntersection(inter: var TIntersection, s: int, zeroInit: bool) = +proc addToIntersection(inter: var TIntersection, s: int, initOnly: bool) = for j in 0..<inter.len: if s == inter[j].id: - inc inter[j].count + if not initOnly: + inc inter[j].count return - if zeroInit: + if initOnly: inter.add((id: s, count: 0)) else: inter.add((id: s, count: 1)) diff --git a/compiler/types.nim b/compiler/types.nim index 3160583787e4..4dc9d36f5f13 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1753,6 +1753,7 @@ proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool = return true case t.kind of tyTuple: + result = false var cycleDetectorCopy: IntSet for i in 0..<t.len: cycleDetectorCopy = cycleDetector From 1cc4d3f6220c5609e38258bd2c5a348e83106be4 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Fri, 25 Aug 2023 22:08:47 +0300 Subject: [PATCH 209/347] fix generic param substitution in templates (#22535) * fix generic param substitution in templates fixes #13527, fixes #17240, fixes #6340, fixes #20033, fixes #19576, fixes #19076 * fix bare except in test, test updated packages in CI --- compiler/sem.nim | 8 +++- compiler/semcall.nim | 7 ++- tests/template/tgenericparam.nim | 80 ++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/template/tgenericparam.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index f69e7a69d05b..653d83aaa682 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -473,7 +473,13 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, # we now know the supplied arguments var paramTypes = initIdTable() for param, value in genericParamsInMacroCall(s, call): - idTablePut(paramTypes, param.typ, value.typ) + var givenType = value.typ + # the sym nodes used for the supplied generic arguments for + # templates and macros leave type nil so regular sem can handle it + # in this case, get the type directly from the sym + if givenType == nil and value.kind == nkSym and value.sym.typ != nil: + givenType = value.sym.typ + idTablePut(paramTypes, param.typ, givenType) retType = generateTypeInstance(c, paramTypes, macroResult.info, retType) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index e7c9226dd2b3..adb87b5b4cd9 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -655,7 +655,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate, else: x.call.add c.graph.emptyNode of skType: - x.call.add newSymNode(s, n.info) + var tn = newSymNode(s, n.info) + # this node will be used in template substitution, + # pretend this is an untyped node and let regular sem handle the type + # to prevent problems where a generic parameter is treated as a value + tn.typ = nil + x.call.add tn else: internalAssert c.config, false diff --git a/tests/template/tgenericparam.nim b/tests/template/tgenericparam.nim new file mode 100644 index 000000000000..d33f55cf70e3 --- /dev/null +++ b/tests/template/tgenericparam.nim @@ -0,0 +1,80 @@ +block: # basic template generic parameter substitution + block: # issue #13527 + template typeNameTempl[T](a: T): string = $T + proc typeNameProc[T](a: T): string = $T + doAssert typeNameTempl(1) == typeNameProc(1) + doAssert typeNameTempl(true) == typeNameProc(true) + doAssert typeNameTempl(1.0) == typeNameProc(1.0) + doAssert typeNameTempl(1u8) == typeNameProc(1u8) + + template isDefault[T](a: T): bool = a == default(T) + doAssert isDefault(0.0) + + block: # issue #17240 + func to(c: int, t: typedesc[float]): t = discard + template converted[I, T](i: seq[I], t: typedesc[T]): seq[T] = + var result = newSeq[T](2) + result[0] = i[0].to(T) + result + doAssert newSeq[int](3).converted(float) == @[0.0, 0.0] + + block: # issue #6340 + type A[T] = object + v: T + proc foo(x: int): string = "int" + proc foo(x: typedesc[int]): string = "typedesc[int]" + template fooT(x: int): string = "int" + template fooT(x: typedesc[int]): string = "typedesc[int]" + proc foo[T](x: A[T]): (string, string) = + (foo(T), fooT(T)) + template fooT[T](x: A[T]): (string, string) = + (foo(T), fooT(T)) + var x: A[int] + doAssert foo(x) == fooT(x) + + block: # issue #20033 + template run[T](): T = default(T) + doAssert run[int]() == 0 + +import options, tables + +block: # complex cases of above with imports + block: # issue #19576, complex case + type RegistryKey = object + key, val: string + var regKey = @[RegistryKey(key: "abc", val: "def")] + template findFirst[T](s: seq[T], pred: proc(x: T): bool): Option[T] = + var res = none(T) # important line + for x in s: + if pred(x): + res = some(x) + break + res + proc getval(searchKey: string): Option[string] = + let found = regKey.findFirst(proc (rk: RegistryKey): bool = rk.key == searchKey) + if found.isNone: none(string) + else: some(found.get().val) + doAssert getval("strange") == none(string) + doAssert getval("abc") == some("def") + block: # issue #19076 + block: # case 1 + var tested: Table[string,int] + template `[]`[V](t:Table[string,V],key:string):untyped = + $V + doAssert tested["abc"] == "int" + template `{}`[V](t:Table[string,V],key:string):untyped = + ($V, tables.`[]`(t, key)) + doAssert (try: tested{"abc"} except KeyError: ("not there", 123)) == ("not there", 123) + tables.`[]=`(tested, "abc", 456) + doAssert tested["abc"] == "int" + doAssert tested{"abc"} == ("int", 456) + block: # case 2 + type Foo[A,T] = object + t:T + proc init[A,T](f:type Foo,a:typedesc[A],t:T):Foo[A,T] = Foo[A,T](t:t) + template fromOption[A](o:Option[A]):auto = + when o.isSome: + Foo.init(A,35) + else: + Foo.init(A,"hi") + let op = fromOption(some(5)) From a108a451c5c4be7158283c08a89691d9684dc578 Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Fri, 25 Aug 2023 17:55:17 -0300 Subject: [PATCH 210/347] Improve compiler cli args (#22509) * . * Fix cli args out of range with descriptive error instead of crash * https://github.com/nim-lang/Nim/pull/22509#issuecomment-1692259451 --- compiler/commands.nim | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 6d162fab26bc..ba996f77ede7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -891,15 +891,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "nodejs") of "maxloopiterationsvm": expectArg(conf, switch, arg, pass, info) - conf.maxLoopIterationsVM = parseInt(arg) + var value: int = 10_000_000 + discard parseSaturatedNatural(arg, value) + if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero") + conf.maxLoopIterationsVM = value of "errormax": expectArg(conf, switch, arg, pass, info) # Note: `nim check` (etc) can overwrite this. # `0` is meaningless, give it a useful meaning as in clang's -ferror-limit # If user doesn't set this flag and the code doesn't either, it'd # have the same effect as errorMax = 1 - let ret = parseInt(arg) - conf.errorMax = if ret == 0: high(int) else: ret + var value: int = 0 + discard parseSaturatedNatural(arg, value) + conf.errorMax = if value == 0: high(int) else: value of "verbosity": expectArg(conf, switch, arg, pass, info) let verbosity = parseInt(arg) @@ -913,7 +917,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.mainPackageNotes = conf.notes of "parallelbuild": expectArg(conf, switch, arg, pass, info) - conf.numberOfProcessors = parseInt(arg) + var value: int = 0 + discard parseSaturatedNatural(arg, value) + conf.numberOfProcessors = value of "version", "v": expectNoArg(conf, switch, arg, pass, info) writeVersionInfo(conf, pass) From c19fd69b693e0e71d8d03812a42c4b8e50c51a3e Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Sun, 27 Aug 2023 12:27:47 +0300 Subject: [PATCH 211/347] test case haul for old generic/template/macro issues (#22564) * test case haul for old generic/template/macro issues closes #12582, closes #19552, closes #2465, closes #4596, closes #15246, closes #12683, closes #7889, closes #4547, closes #12415, closes #2002, closes #1771, closes #5121 The test for #5648 is also moved into its own test from `types/tissues_types` due to not being joinable. * fix template gensym test --- tests/generics/treentranttypes.nim | 11 ++++ .../generics/tuninstantiatedgenericcalls.nim | 62 +++++++++++++++++++ tests/macros/tmacros_various.nim | 31 ++++++++++ tests/template/mdotcall.nim | 32 ++++++++++ tests/template/tdotcall.nim | 12 ++++ tests/template/template_various.nim | 36 +++++++++++ tests/template/tobjectdeclfield.nim | 25 +++++--- tests/types/t5648.nim | 32 ++++++++++ tests/types/tissues_types.nim | 41 +++++------- 9 files changed, 248 insertions(+), 34 deletions(-) create mode 100644 tests/types/t5648.nim diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim index 40ff1647b95b..801f0e444f7c 100644 --- a/tests/generics/treentranttypes.nim +++ b/tests/generics/treentranttypes.nim @@ -101,3 +101,14 @@ echo @(b.arr[0].arr), @(b.arr[1].arr) let y = b echo @(y.arr[0].arr), @(y.arr[1].arr) +import macros + +block: # issue #5121 + type + A = object + AConst[X] = A + + macro dumpType(t: typedesc): untyped = + result = newTree(nnkTupleConstr, newLit $t.getType[1].typeKind, newLit t.getType[1].treeRepr) + + doAssert dumpType(A) == ("ntyObject", "Sym \"A\"") diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim index b12a33fe799d..bac334e95592 100644 --- a/tests/generics/tuninstantiatedgenericcalls.nim +++ b/tests/generics/tuninstantiatedgenericcalls.nim @@ -78,3 +78,65 @@ block: doAssert x.data.len == 5 var y: Leb128Buf[uint16] doAssert y.data.len == 3 + +import macros + +block: # issue #12415 + macro isSomePointerImpl(t: typedesc): bool = + var impl = t.getTypeInst[1].getTypeImpl + if impl.kind == nnkDistinctTy: + impl = impl[0].getTypeImpl + if impl.kind in {nnkPtrTy,nnkRefTy}: + result = newLit(true) + elif impl.kind == nnkSym and impl.eqIdent("pointer"): + result = newLit(true) + else: + result = newLit(false) + + proc isSomePointer[T](t: typedesc[T]): bool {.compileTime.} = + isSomePointerImpl(t) + + type + Option[T] = object + ## An optional type that stores its value and state separately in a boolean. + when isSomePointer(typedesc(T)): + val: T + else: + val: T + has: bool + var x: Option[ref int] + doAssert not compiles(x.has) + var y: Option[int] + doAssert compiles(y.has) + +block: # issue #2002 + proc isNillable(T: typedesc): bool = + when compiles((let v: T = nil)): + return true + else: + return false + + type + Foo[T] = object + when isNillable(T): + nillable: float + else: + notnillable: int + + var val1: Foo[ref int] + doAssert compiles(val1.nillable) + doAssert not compiles(val1.notnillable) + var val2: Foo[int] + doAssert not compiles(val2.nillable) + doAssert compiles(val2.notnillable) + +block: # issue #1771 + type + Foo[X, T] = object + bar: array[X.low..X.high, T] + + proc test[X, T](f: Foo[X, T]): T = + f.bar[X.low] + + var a: Foo[range[0..2], float] + doAssert test(a) == 0.0 diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 2446912ea346..e351b452716f 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -268,6 +268,37 @@ xbenchmark: discard inputtest fastSHA("hey") +block: # issue #4547 + macro lazy(stmtList : typed) : untyped = + let decl = stmtList[0] + decl.expectKind nnkLetSection + let name = decl[0][0].strVal + let call = decl[0][2].copy + call.expectKind nnkCall + let ident = newIdentNode("get" & name) + result = quote do: + var value : type(`call`) + proc `ident`() : type(`call`) = + if value.isNil: + value = `call` + value + type MyObject = object + a,b: int + # this part, the macro call and it's result (written in the comment below) is important + lazy: + let y = new(MyObject) + #[ + var value: type(new(MyObject)) + proc gety(): type(new(MyObject)) = + if value.isNil: + value = new(MyObject) + value + ]# + doAssert gety().a == 0 # works and should work + doAssert gety().b == 0 # works and should work + doAssert not declared(y) + doAssert not compiles(y.a) # identifier y should not exist anymore + doAssert not compiles(y.b) # identifier y should not exist anymore block: # bug #13511 type diff --git a/tests/template/mdotcall.nim b/tests/template/mdotcall.nim index 13dcbd82489e..fecd8ee2661b 100644 --- a/tests/template/mdotcall.nim +++ b/tests/template/mdotcall.nim @@ -48,3 +48,35 @@ template publicTemplateObjSyntax*(o: var ObjA, arg: Natural, doStuff: untyped) = o.foo2() doStuff o.bar2(arg) + +# issue #15246 +import os + +template sourceBaseName*(): string = + bind splitFile + instantiationInfo().filename.splitFile().name + +# issue #12683 + +import unicode +template toRune(s: string): Rune = s.runeAt(0) +proc heh*[T](x: Slice[T], chars: string) = discard chars.toRune + +# issue #7889 + +from streams import newStringStream, readData, writeData + +template bindmeTemplate*(): untyped = + var tst = "sometext" + var ss = newStringStream("anothertext") + ss.writeData(tst[0].addr, 2) + discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful + +from macros import quote, newIdentNode + +macro bindmeQuote*(): untyped = + quote do: + var tst = "sometext" + var ss = newStringStream("anothertext") + ss.writeData(tst[0].addr, 2) + discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful diff --git a/tests/template/tdotcall.nim b/tests/template/tdotcall.nim index 5fc991dd2dcb..dc97fd52e6cd 100644 --- a/tests/template/tdotcall.nim +++ b/tests/template/tdotcall.nim @@ -18,3 +18,15 @@ block: # issue #11733 var evaluated = false a.publicTemplateObjSyntax(42): evaluated = true doAssert evaluated + +block: # issue #15246 + doAssert sourceBaseName() == "tdotcall" + +block: # issue #12683 + heh(0..40, "|") + +block: # issue #7889 + if false: + bindmeQuote() + if false: + bindmeTemplate() diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index a3b549e181be..2088b1739f7d 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -354,6 +354,23 @@ block gensym3: echo a ! b ! c ! d ! e echo x,y,z +block: # issue #2465 + template t() = + template declX(str: string) {.gensym.} = + var x {.inject.} : string = str + + t() + doAssert not declared(declX) + doAssert not compiles(declX("a string")) + + template t2() = + template fooGensym() {.gensym.} = + echo 42 + + t2() + doAssert not declared(fooGensym) + doAssert not compiles(fooGensym()) + block identifier_construction_with_overridden_symbol: # could use add, but wanna make sure it's an override no matter what @@ -368,3 +385,22 @@ block identifier_construction_with_overridden_symbol: `examplefn n`() exampletempl(1) + +import typetraits + +block: # issue #4596 + type + T0 = object + T1 = object + + template printFuncsT() = + proc getV[A](a: typedesc[A]): string = + var s {. global .} = name(A) + return s + + printFuncsT() + + doAssert getV(T1) == "T1" + doAssert getV(T0) == "T0" + doAssert getV(T0) == "T0" + doAssert getV(T1) == "T1" diff --git a/tests/template/tobjectdeclfield.nim b/tests/template/tobjectdeclfield.nim index 201f076ca364..afce2cae8112 100644 --- a/tests/template/tobjectdeclfield.nim +++ b/tests/template/tobjectdeclfield.nim @@ -1,12 +1,21 @@ -var x = 0 +block: # issue #16005 + var x = 0 -block: - type Foo = object - x: float # ok - -template main() = block: type Foo = object - x: float # Error: cannot use symbol of kind 'var' as a 'field' + x: float # ok + + template main() = + block: + type Foo = object + x: float # Error: cannot use symbol of kind 'var' as a 'field' + + main() + +block: # issue #19552 + template test = + type + test2 = ref object + reset: int -main() + test() diff --git a/tests/types/t5648.nim b/tests/types/t5648.nim new file mode 100644 index 000000000000..b3bd406b3f7c --- /dev/null +++ b/tests/types/t5648.nim @@ -0,0 +1,32 @@ +discard """ + output: ''' +ptr Foo +''' +joinable: false +""" +# not joinable because it causes out of memory with --gc:boehm + +# issue #5648 + +import typetraits + +type Foo = object + bar: int + +proc main() = + var f = create(Foo) + f.bar = 3 + echo f.type.name + + discard realloc(f, 0) + + var g = Foo() + g.bar = 3 + +var + mainPtr = cast[pointer](main) + mainFromPtr = cast[typeof(main)](mainPtr) + +doAssert main == mainFromPtr + +main() diff --git a/tests/types/tissues_types.nim b/tests/types/tissues_types.nim index 275941caec48..eab4e8e9be98 100644 --- a/tests/types/tissues_types.nim +++ b/tests/types/tissues_types.nim @@ -3,7 +3,6 @@ discard """ true true true -ptr Foo (member: "hello world") (member: 123.456) (member: "hello world", x: ...) @@ -11,10 +10,7 @@ ptr Foo 0 false ''' -joinable: false """ -# not joinable because it causes out of memory with --gc:boehm -import typetraits block t1252: echo float32 isnot float64 @@ -29,28 +25,6 @@ block t5640: var v = vec2([0.0'f32, 0.0'f32]) -block t5648: - type Foo = object - bar: int - - proc main() = - var f = create(Foo) - f.bar = 3 - echo f.type.name - - discard realloc(f, 0) - - var g = Foo() - g.bar = 3 - - var - mainPtr = cast[pointer](main) - mainFromPtr = cast[typeof(main)](mainPtr) - - doAssert main == mainFromPtr - - main() - block t7581: discard int -1 @@ -107,3 +81,18 @@ block: var f1: Foo echo f1.bar + +import macros + +block: # issue #12582 + macro foo(T: type): type = + nnkBracketExpr.newTree(bindSym "array", newLit 1, T) + var + _: foo(int) # fine + type + Foo = object + x: foo(int) # fine + Bar = ref object + x: foo(int) # error + let b = Bar() + let b2 = Bar(x: [123]) From 0b78b7f595ef96a9769e0d59167239c611b9857a Mon Sep 17 00:00:00 2001 From: Bung <crc32@qq.com> Date: Sun, 27 Aug 2023 20:29:24 +0800 Subject: [PATCH 212/347] =?UTF-8?q?fix=20#22548;environment=20misses=20for?= =?UTF-8?q?=20type=20reference=20in=20iterator=20access=20n=E2=80=A6=20(#2?= =?UTF-8?q?2559)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #22548;environment misses for type reference in iterator access nested in closure * fix #21737 * Update lambdalifting.nim * remove containsCallKinds * simplify --- compiler/lambdalifting.nim | 24 +++++++++++++++++++----- tests/iter/t21737.nim | 22 ++++++++++++++++++++++ tests/iter/t22548.nim | 21 +++++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 tests/iter/t21737.nim create mode 100644 tests/iter/t22548.nim diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ee1e5b7976bb..ee19eec08147 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -316,6 +316,7 @@ type processed, capturedVars: IntSet ownerToType: Table[int, PType] somethingToDo: bool + inTypeOf: bool graph: ModuleGraph idgen: IdGenerator @@ -417,6 +418,9 @@ Consider: """ +proc isTypeOf(n: PNode): bool = + n.kind == nkSym and n.sym.magic in {mTypeOf, mType} + proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = var cp = getEnvParam(fn) let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner @@ -455,7 +459,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = c.somethingToDo = true addClosureParam(c, owner, n.info) if interestingIterVar(s): - if not c.capturedVars.containsOrIncl(s.id): + if not c.capturedVars.contains(s.id): + if not c.inTypeOf: c.capturedVars.incl(s.id) let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr}) @@ -481,10 +486,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = addClosureParam(c, owner, n.info) #echo "capturing ", n.info # variable 's' is actually captured: - if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): - let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) - #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) - discard addField(obj, s, c.graph.cache, c.idgen) + if interestingVar(s): + if not c.capturedVars.contains(s.id): + if not c.inTypeOf: c.capturedVars.incl(s.id) + let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) + #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) + discard addField(obj, s, c.graph.cache, c.idgen) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: @@ -516,9 +523,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = detectCapturedVars(n[namePos], owner, c) of nkReturnStmt: detectCapturedVars(n[0], owner, c) + of nkIdentDefs: + detectCapturedVars(n[^1], owner, c) else: + if n.isCallExpr and n[0].isTypeOf: + c.inTypeOf = true for i in 0..<n.len: detectCapturedVars(n[i], owner, c) + c.inTypeOf = false type LiftingPass = object @@ -798,6 +810,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; of nkTypeOfExpr: result = n else: + if n.isCallExpr and n[0].isTypeOf: + return if owner.isIterator: if nfLL in n.flags: # special case 'when nimVm' due to bug #3636: diff --git a/tests/iter/t21737.nim b/tests/iter/t21737.nim new file mode 100644 index 000000000000..da06faea7167 --- /dev/null +++ b/tests/iter/t21737.nim @@ -0,0 +1,22 @@ +discard """ + action: compile +""" + +template mytoSeq*(iter: untyped): untyped = + var result: seq[typeof(iter)]# = @[] + for x in iter: + result.add(x) + result + +iterator test(dir:int): int = + yield 1234 + +iterator walkGlobKinds (): int = + let dir2 = 123 + let it = mytoSeq(test(dir2)) + +proc main()= + let it = iterator(): int= + for path in walkGlobKinds(): + yield path +main() diff --git a/tests/iter/t22548.nim b/tests/iter/t22548.nim new file mode 100644 index 000000000000..b9abb75d0314 --- /dev/null +++ b/tests/iter/t22548.nim @@ -0,0 +1,21 @@ +discard """ + action: compile +""" + +type Xxx[T] = object + +iterator x(v: string): char = + var v2: Xxx[int] + + var y: v2.T + + echo y + +proc bbb(vv: string): proc () = + proc xxx() = + for c in x(vv): + echo c + + return xxx + +bbb("test")() From 100eb6820c271bbd1c02a8e6a9001fc5a08a2637 Mon Sep 17 00:00:00 2001 From: Bung <crc32@qq.com> Date: Sun, 27 Aug 2023 22:56:50 +0800 Subject: [PATCH 213/347] close #9334 (#22565) --- tests/closure/t9334.nim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/closure/t9334.nim diff --git a/tests/closure/t9334.nim b/tests/closure/t9334.nim new file mode 100644 index 000000000000..36a9a7d77c52 --- /dev/null +++ b/tests/closure/t9334.nim @@ -0,0 +1,19 @@ +discard """ + cmd: "nim $target --hints:off $options -r $file" + nimout: '''@[1] +@[1, 1] +''' + nimoutFull: true +""" +proc p(s: var seq[int]): auto = + let sptr = addr s + return proc() = sptr[].add 1 + +proc f = + var data = @[1] + p(data)() + echo repr data + +static: + f() # prints [1] +f() # prints [1, 1] From 094a29eb315b571924bb3b381eb25df8fb9c193c Mon Sep 17 00:00:00 2001 From: Bung <crc32@qq.com> Date: Mon, 28 Aug 2023 12:31:16 +0800 Subject: [PATCH 214/347] add test case for #19095 (#22566) --- tests/closure/t19095.nim | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/closure/t19095.nim diff --git a/tests/closure/t19095.nim b/tests/closure/t19095.nim new file mode 100644 index 000000000000..880456e0293b --- /dev/null +++ b/tests/closure/t19095.nim @@ -0,0 +1,35 @@ +discard """ + action: compile +""" + +block: + func inCheck() = + discard + + iterator iter(): int = + yield 0 + yield 0 + + func search() = + let inCheck = 0 + + for i in iter(): + + proc hello() = + inCheck() + + search() +block: + iterator iter(): int = + yield 0 + yield 0 + + func search() = + let lmrMoveCounter = 0 + + for i in iter(): + + proc hello() = + discard lmrMoveCounter + + search() From 306b9aca485bfc90931218c4f050863bcbe6e6c0 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 28 Aug 2023 15:57:24 +0800 Subject: [PATCH 215/347] `initCandidate` and friends now return values (#22570) * `initCandidate` and friends now return values * fixes semexprs.nim * fixes semcall.nim * Update compiler/semcall.nim --- compiler/semcall.nim | 8 +++--- compiler/semexprs.nim | 3 +-- compiler/sigmatch.nim | 63 +++++++++++++++++++------------------------ 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index adb87b5b4cd9..adfc98e1973d 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -51,9 +51,9 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode, result.add((symx, o.lastOverloadScope)) symx = nextOverloadIter(o, c, headSymbol) if result.len > 0: - initCandidate(c, best, result[0].s, initialBinding, + best = initCandidate(c, result[0].s, initialBinding, result[0].scope, diagnostics) - initCandidate(c, alt, result[0].s, initialBinding, + alt = initCandidate(c, result[0].s, initialBinding, result[0].scope, diagnostics) best.state = csNoMatch @@ -82,10 +82,10 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, # starts at 1 because 0 is already done with setup, only needs checking var nextSymIndex = 1 - var z: TCandidate = default(TCandidate) # current candidate + var z: TCandidate # current candidate while true: determineType(c, sym) - initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag) + z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag) # this is kinda backwards as without a check here the described # problems in recalc would not happen, but instead it 100% diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cb4bddb5d1f8..ff9727967fa3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -992,8 +992,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, proc resolveIndirectCall(c: PContext; n, nOrig: PNode; t: PType): TCandidate = - result = default(TCandidate) - initCandidate(c, result, t) + result = initCandidate(c, t) matches(c, n, nOrig, result) proc bracketedMacro(n: PNode): PSym = diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 7780e53f538b..bb99463b64dd 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -100,25 +100,18 @@ proc markOwnerModuleAsUsed*(c: PContext; s: PSym) template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone proc initCandidateAux(ctx: PContext, - c: var TCandidate, callee: PType) {.inline.} = - c.c = ctx - c.exactMatches = 0 - c.subtypeMatches = 0 - c.convMatches = 0 - c.intConvMatches = 0 - c.genericMatches = 0 - c.state = csEmpty - c.firstMismatch = MismatchInfo() - c.callee = callee - c.call = nil - c.baseTypeMatch = false - c.genericConverter = false - c.inheritancePenalty = 0 - -proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) = - initCandidateAux(ctx, c, callee) - c.calleeSym = nil - c.bindings = initIdTable() + callee: PType): TCandidate {.inline.} = + result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0, + convMatches: 0, intConvMatches: 0, genericMatches: 0, + state: csEmpty, firstMismatch: MismatchInfo(), + callee: callee, call: nil, baseTypeMatch: false, + genericConverter: false, inheritancePenalty: 0 + ) + +proc initCandidate*(ctx: PContext, callee: PType): TCandidate = + result = initCandidateAux(ctx, callee) + result.calleeSym = nil + result.bindings = initIdTable() proc put(c: var TCandidate, key, val: PType) {.inline.} = ## Given: proc foo[T](x: T); foo(4) @@ -134,27 +127,27 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = echo "binding ", key, " -> ", val idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) -proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, +proc initCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1, - diagnosticsEnabled = false) = - initCandidateAux(ctx, c, callee.typ) - c.calleeSym = callee + diagnosticsEnabled = false): TCandidate = + result = initCandidateAux(ctx, callee.typ) + result.calleeSym = callee if callee.kind in skProcKinds and calleeScope == -1: if callee.originatingModule == ctx.module: - c.calleeScope = 2 + result.calleeScope = 2 var owner = callee while true: owner = owner.skipGenericOwner if owner.kind == skModule: break - inc c.calleeScope + inc result.calleeScope else: - c.calleeScope = 1 + result.calleeScope = 1 else: - c.calleeScope = calleeScope - c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil - c.diagnosticsEnabled = diagnosticsEnabled - c.magic = c.calleeSym.magic - c.bindings = initIdTable() + result.calleeScope = calleeScope + result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil + result.diagnosticsEnabled = diagnosticsEnabled + result.magic = result.calleeSym.magic + result.bindings = initIdTable() if binding != nil and callee.kind in routineKinds: var typeParams = callee.ast[genericParamsPos] for i in 1..min(typeParams.len, binding.len-1): @@ -166,16 +159,14 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, bound = makeTypeDesc(ctx, bound) else: bound = bound.skipTypes({tyTypeDesc}) - put(c, formalTypeParam, bound) + put(result, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = - result = default(TCandidate) - initCandidate(ctx, result, callee, binding, calleeScope) + result = initCandidate(ctx, callee, binding, calleeScope) proc newCandidate*(ctx: PContext, callee: PType): TCandidate = - result = default(TCandidate) - initCandidate(ctx, result, callee) + result = initCandidate(ctx, callee) proc copyCandidate(a: var TCandidate, b: TCandidate) = a.c = b.c From 2e7c8a339f654ebd6fe178f4c81aa89c14851f22 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 28 Aug 2023 16:43:58 +0800 Subject: [PATCH 216/347] newStringOfCap now won't initialize all elements anymore (#22568) newStringOfCap nows won't initialize all elements anymore --- lib/system/strs_v2.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 296aae045f15..1c15d14711d6 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -108,10 +108,11 @@ proc rawNewString(space: int): NimStringV2 {.compilerproc.} = result = NimStringV2(len: 0, p: nil) else: when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared0(contentSize(space))) + var p = cast[ptr NimStrPayload](allocShared(contentSize(space))) else: - var p = cast[ptr NimStrPayload](alloc0(contentSize(space))) + var p = cast[ptr NimStrPayload](alloc(contentSize(space))) p.cap = space + p.data[0] = '\0' result = NimStringV2(len: 0, p: p) proc mnewString(len: int): NimStringV2 {.compilerproc.} = From 94454addb2a045731f2b4f44d697a319e3a20071 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Mon, 28 Aug 2023 16:09:43 +0300 Subject: [PATCH 217/347] define toList procs after add for lists [backport] (#22573) fixes #22543 --- lib/pure/collections/lists.nim | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index e1d32e73728f..b9d5c48eb5cd 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -165,28 +165,6 @@ proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] = new(result) result.value = value -func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = - ## Creates a new `SinglyLinkedList` from the members of `elems`. - runnableExamples: - from std/sequtils import toSeq - let a = [1, 2, 3, 4, 5].toSinglyLinkedList - assert a.toSeq == [1, 2, 3, 4, 5] - - result = initSinglyLinkedList[T]() - for elem in elems.items: - result.add(elem) - -func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = - ## Creates a new `DoublyLinkedList` from the members of `elems`. - runnableExamples: - from std/sequtils import toSeq - let a = [1, 2, 3, 4, 5].toDoublyLinkedList - assert a.toSeq == [1, 2, 3, 4, 5] - - result = initDoublyLinkedList[T]() - for elem in elems.items: - result.add(elem) - template itemsListImpl() {.dirty.} = var it {.cursor.} = L.head while it != nil: @@ -993,3 +971,25 @@ proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_ ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_ a.addMoved(b) + +func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a new `SinglyLinkedList` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toSinglyLinkedList + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initSinglyLinkedList[T]() + for elem in elems.items: + result.add(elem) + +func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a new `DoublyLinkedList` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toDoublyLinkedList + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initDoublyLinkedList[T]() + for elem in elems.items: + result.add(elem) From 3de8d755135d94983ca087f448ad76832c341eaa Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Mon, 28 Aug 2023 22:40:46 +0300 Subject: [PATCH 218/347] correct logic for qualified symbol in templates (#22577) * correct logic for qualified symbol in templates fixes #19865 * add test --- compiler/semtempl.nim | 5 ++++- tests/template/template_issues.nim | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 85411f7c4696..eec02812226e 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -567,6 +567,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # so we use the generic code for nkDotExpr too let s = qualifiedLookUp(c.c, n, {}) if s != nil: + # mirror the nkIdent case # do not symchoice a quoted template parameter (bug #2390): if s.owner == c.owner and s.kind == skParam and n.kind == nkAccQuoted and n.len == 1: @@ -578,7 +579,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = elif contains(c.toMixin, s.name.id): return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - return symChoice(c.c, n, s, scOpen, c.noGenSym > 0) + if s.kind in {skType, skVar, skLet, skConst}: + discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) + return semTemplSymbol(c.c, n, s, c.noGenSym > 0) if n.kind == nkDotExpr: result = n result[0] = semTemplBody(c, n[0]) diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index 58c40941db69..5b7c54ed6449 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -302,3 +302,7 @@ block: # bug #21920 discard t[void]() # Error: expression has no type: discard + +block: # issue #19865 + template f() = discard default(system.int) + f() From 6b955ac4af834fb9765b5b2a2588a5feb1de31f0 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Mon, 28 Aug 2023 22:41:18 +0300 Subject: [PATCH 219/347] properly fold constants for dynlib pragma (#22575) fixes #12929 --- compiler/pragmas.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 6a371ba06797..49b3b819e4f0 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -333,7 +333,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode = # {.dynlib: myGetProcAddr(...).} result = c.semExpr(c, n[1]) if result.kind == nkSym and result.sym.kind == skConst: - result = result.sym.astdef # look it up + result = c.semConstExpr(c, result) # fold const if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}: localError(c.config, n.info, errStringLiteralExpected) result = newEmptyStrNode(c, n) From d8ffc6a75edbed49e24f9ed5c9eff892eefc3ee7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:59:51 +0800 Subject: [PATCH 220/347] minor style changes in the compiler (#22584) * minor style changes in the compiler * use raiseAssert --- compiler/vm.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 79832dbcb524..18b26486587b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1392,8 +1392,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let prcValue = c.globals[prc.position-1] if prcValue.kind == nkEmpty: globalError(c.config, c.debug[pc], "cannot run " & prc.name.s) - var slots2: TNodeSeq = default(TNodeSeq) - slots2.setLen(tos.slots.len) + var slots2: TNodeSeq = newSeq[PNode](tos.slots.len) for i in 0..<tos.slots.len: slots2[i] = regToNode(tos.slots[i]) let newValue = callForeignFunction(c.config, prcValue, prc.typ, slots2, @@ -1482,7 +1481,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcExcept: # This opcode is never executed, it only holds information for the # exception handling routines. - doAssert(false) + raiseAssert "unreachable" of opcFinally: # Pop the last safepoint introduced by a opcTry. This opcode is only # executed _iff_ no exception was raised in the body of the `try` From 1fcb53cded47a9671671170f0df42f6efbde5be4 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 29 Aug 2023 16:40:19 +0800 Subject: [PATCH 221/347] fixes broken nightlies; follow up #22544 (#22585) ref https://github.com/nim-lang/nightlies/actions/runs/5970369118/job/16197865657 > /home/runner/work/nightlies/nightlies/nim/lib/pure/os.nim(678, 30) Error: getApplOpenBsd() can raise an unlisted exception: ref OSError --- lib/pure/os.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 13b103b92598..7ba156c89f94 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -675,7 +675,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW elif defined(haiku): result = getApplHaiku() elif defined(openbsd): - result = getApplOpenBsd() + result = try: getApplOpenBsd() except OSError: "" elif defined(nintendoswitch): result = "" From e53c66ef39e9f6b521835399677430c2c980ee64 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 29 Aug 2023 19:29:42 +0800 Subject: [PATCH 222/347] fixes #22555; implements `newStringUninit` (#22572) * fixes newStringUninitialized; implement `newStringUninitialized` * add a simple test case * adds a changelog * Update lib/system.nim * Apply suggestions from code review rename to newStringUninit --- changelog.md | 2 + lib/system.nim | 58 ++++++++++++++--------- tests/system/tnewstring_uninitialized.nim | 11 +++++ 3 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 tests/system/tnewstring_uninitialized.nim diff --git a/changelog.md b/changelog.md index b4b8ca532da9..fb0102cd3af1 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,8 @@ [//]: # "Additions:" +- Adds `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. + [//]: # "Deprecations:" diff --git a/lib/system.nim b/lib/system.nim index 4163534cf629..c3cad4f7119a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -646,26 +646,6 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {. ## ``` discard -when not defined(js): - proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] = - ## Creates a new sequence of type `seq[T]` with length `len`. - ## - ## Only available for numbers types. Note that the sequence will be - ## uninitialized. After the creation of the sequence you should assign - ## entries to the sequence instead of adding them. - ## Example: - ## ```nim - ## var x = newSeqUninitialized[int](3) - ## assert len(x) == 3 - ## x[0] = 10 - ## ``` - result = newSeqOfCap[T](len) - when defined(nimSeqsV2): - cast[ptr int](addr result)[] = len - else: - var s = cast[PGenericSeq](result) - s.len = len - func len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.magic: "LengthOpenArray".} = ## Returns the length of an openArray. runnableExamples: @@ -973,8 +953,8 @@ proc setLen*(s: var string, newlen: Natural) {. proc newString*(len: Natural): string {. magic: "NewString", importc: "mnewString", noSideEffect.} - ## Returns a new string of length `len` but with uninitialized - ## content. One needs to fill the string character after character + ## Returns a new string of length `len`. + ## One needs to fill the string character after character ## with the index operator `s[i]`. ## ## This procedure exists only for optimization purposes; @@ -1630,6 +1610,40 @@ when notJSnotNims and defined(nimSeqsV2): include "system/strs_v2" include "system/seqs_v2" +when not defined(js): + proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] = + ## Creates a new sequence of type `seq[T]` with length `len`. + ## + ## Only available for numbers types. Note that the sequence will be + ## uninitialized. After the creation of the sequence you should assign + ## entries to the sequence instead of adding them. + ## Example: + ## ```nim + ## var x = newSeqUninitialized[int](3) + ## assert len(x) == 3 + ## x[0] = 10 + ## ``` + result = newSeqOfCap[T](len) + when defined(nimSeqsV2): + cast[ptr int](addr result)[] = len + else: + var s = cast[PGenericSeq](result) + s.len = len + + proc newStringUninit*(len: Natural): string = + ## Returns a new string of length `len` but with uninitialized + ## content. One needs to fill the string character after character + ## with the index operator `s[i]`. + ## + ## This procedure exists only for optimization purposes; + ## the same effect can be achieved with the `&` operator or with `add`. + result = newStringOfCap(len) + when defined(nimSeqsV2): + cast[ptr int](addr result)[] = len + else: + var s = cast[PGenericSeq](result) + s.len = len + {.pop.} when not defined(nimscript): diff --git a/tests/system/tnewstring_uninitialized.nim b/tests/system/tnewstring_uninitialized.nim new file mode 100644 index 000000000000..9bc0e162298e --- /dev/null +++ b/tests/system/tnewstring_uninitialized.nim @@ -0,0 +1,11 @@ +discard """ + matrix: "--mm:refc;" +""" + +# bug #22555 +var x = newStringUninit(10) +doAssert x.len == 10 +for i in 0..<x.len: + x[i] = chr(ord('a') + i) + +doAssert x == "abcdefghij" From b6cea7b599b81db675b95d2f84a8e3cda071cb0d Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Tue, 29 Aug 2023 15:59:49 +0300 Subject: [PATCH 223/347] clearer error for different size int/float cast in VM (#22582) refs #16547 --- compiler/vmgen.nim | 14 +++++++++++--- tests/vm/tunsupportedintfloatcast.nim | 3 +++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/vm/tunsupportedintfloatcast.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index e58ddbeb91d3..7df14f1702df 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -890,6 +890,8 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = let dst = n[0].typ.skipTypes(abstractRange)#.kind let srcSize = getSize(c.config, src) let dstSize = getSize(c.config, dst) + const unsupportedCastDifferentSize = + "VM does not support 'cast' from $1 with size $2 to $3 with size $4 due to different sizes" if src.kind in allowedIntegers and dst.kind in allowedIntegers: let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) @@ -906,8 +908,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = # is smaller than source. c.gABC(n, opcNarrowU, dest, TRegister(dstSize*8)) c.freeTemp(tmp) - elif srcSize == dstSize and src.kind in allowedIntegers and - dst.kind in {tyFloat, tyFloat32, tyFloat64}: + elif src.kind in allowedIntegers and + dst.kind in {tyFloat, tyFloat32, tyFloat64}: + if srcSize != dstSize: + globalError(c.config, n.info, unsupportedCastDifferentSize % + [$src.kind, $srcSize, $dst.kind, $dstSize]) let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) if dst.kind == tyFloat32: @@ -916,8 +921,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = c.gABC(n, opcCastIntToFloat64, dest, tmp) c.freeTemp(tmp) - elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and + elif src.kind in {tyFloat, tyFloat32, tyFloat64} and dst.kind in allowedIntegers: + if srcSize != dstSize: + globalError(c.config, n.info, unsupportedCastDifferentSize % + [$src.kind, $srcSize, $dst.kind, $dstSize]) let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) if src.kind == tyFloat32: diff --git a/tests/vm/tunsupportedintfloatcast.nim b/tests/vm/tunsupportedintfloatcast.nim new file mode 100644 index 000000000000..d65f10d8674f --- /dev/null +++ b/tests/vm/tunsupportedintfloatcast.nim @@ -0,0 +1,3 @@ +static: + echo cast[int32](12.0) #[tt.Error + ^ VM does not support 'cast' from tyFloat with size 8 to tyInt32 with size 4 due to different sizes]# From a7a0105d8c538ce9d0ac26ba73409107d55d3c8c Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 29 Aug 2023 21:00:13 +0800 Subject: [PATCH 224/347] deprecate `std/threadpool`; use `malebolgia`, `weave`, `nim-taskpool` instead (#22576) * deprecate `std/threadpool`; use `malebolgia` instead * Apply suggestions from code review * Apply suggestions from code review * change the URL of inim --- lib/pure/concurrency/threadpool.nim | 2 ++ testament/important_packages.nim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 136850b4f0ab..e34162fa4cd5 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -7,6 +7,8 @@ # distribution, for details about the copyright. # +{.deprecated: "use the nimble packages `malebolgia`, `taskpools` or `weave` instead".} + ## Implements Nim's `parallel & spawn statements <manual_experimental.html#parallel-amp-spawn>`_. ## ## Unstable API. diff --git a/testament/important_packages.nim b/testament/important_packages.nim index f831311beeda..ff02c1783ba1 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -80,7 +80,7 @@ pkg "gnuplot", "nim c gnuplot.nim" pkg "hts", "nim c -o:htss src/hts.nim" pkg "httpauth" pkg "illwill", "nimble examples" -pkg "inim" +pkg "inim", url = "https://github.com/nim-lang/INim" pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" pkg "jstin" From d7634c1bd42dd5367d10283a2efc353a0a83aed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Wed, 30 Aug 2023 06:22:36 +0100 Subject: [PATCH 225/347] fixes an issue where sometimes wasMoved produced bad codegen for cpp (#22587) --- compiler/ccgexprs.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index be04cee9ee44..2492eebaee9f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2324,7 +2324,10 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = s = "$1, $1Len_0" % [rdLoc(a)] linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s]) else: - linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)]) + if p.module.compileToCpp: + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), rdLoc(a)]) + else: + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)]) else: let flags = if not canMove(p, n[1], d): {needToCopy} else: {} genAssignment(p, d, a, flags) From 2e4e2f8f5076b39e5976ba20f231f468b1b5052c Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Wed, 30 Aug 2023 08:23:14 +0300 Subject: [PATCH 226/347] handle typedesc params in VM (#22581) * handle typedesc params in VM fixes #15760 * add test * fix getType(typedesc) test --- compiler/vmgen.nim | 8 +++++++- tests/vm/ttypedesc.nim | 18 ++++++++++++++++++ tests/vm/tvmmisc.nim | 5 +++-- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 tests/vm/ttypedesc.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7df14f1702df..1f51b26cc264 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1546,6 +1546,7 @@ proc checkCanEval(c: PCtx; n: PNode) = # little hack ahead for bug #12612: assume gensym'ed variables # are in the right scope: if sfGenSym in s.flags and c.prc.sym == nil: discard + elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard else: cannotEval(c, n) elif s.kind in {skProc, skFunc, skConverter, skMethod, skIterator} and sfForward in s.flags: @@ -2102,8 +2103,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = let s = n.sym checkCanEval(c, n) case s.kind - of skVar, skForVar, skTemp, skLet, skParam, skResult: + of skVar, skForVar, skTemp, skLet, skResult: genRdVar(c, n, dest, flags) + of skParam: + if s.typ.kind == tyTypeDesc: + genTypeLit(c, s.typ, dest) + else: + genRdVar(c, n, dest, flags) of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator: # 'skTemplate' is only allowed for 'getAst' support: if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: diff --git a/tests/vm/ttypedesc.nim b/tests/vm/ttypedesc.nim new file mode 100644 index 000000000000..a112584c544a --- /dev/null +++ b/tests/vm/ttypedesc.nim @@ -0,0 +1,18 @@ +block: # issue #15760 + type + Banana = object + SpecialBanana = object + + proc getName(_: type Banana): string = "Banana" + proc getName(_: type SpecialBanana): string = "SpecialBanana" + + proc x[T](): string = + const n = getName(T) # this one works + result = n + + proc y(T: type): string = + const n = getName(T) # this one failed to compile + result = n + + doAssert x[SpecialBanana]() == "SpecialBanana" + doAssert y(SpecialBanana) == "SpecialBanana" diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 5760422a1bd0..1429ef6e97e3 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -1,10 +1,11 @@ -# bug #4462 import macros import os +# bug #4462 block: proc foo(t: typedesc) {.compileTime.} = - assert sameType(getType(t), getType(int)) + assert sameType(getType(t), getType(typedesc[int])) + assert sameType(getType(t), getType(type int)) static: foo(int) From dfb3a88cc3a077b452c90e63af3dec21ba822181 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 31 Aug 2023 15:26:09 +0800 Subject: [PATCH 227/347] fixes yaml tests (#22595) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index ff02c1783ba1..a21ea41b705d 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -174,6 +174,6 @@ pkg "websocket", "nim c websocket.nim" pkg "winim", "nim c winim.nim" pkg "with" pkg "ws", allowFailure = true -pkg "yaml", "nim c -r test/tserialization.nim" +pkg "yaml" pkg "zero_functional", "nim c -r test.nim" pkg "zippy" From 5bd1afc3f9716fed833b7bd251ee45479b78a950 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 31 Aug 2023 19:04:32 +0800 Subject: [PATCH 228/347] fixes #17197; fixes #22560; fixes the dest of newSeqOfCap in refc (#22594) --- compiler/ccgexprs.nim | 1 + tests/collections/tseq.nim | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2492eebaee9f..8bd0b872158a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1468,6 +1468,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = getSeqPayloadType(p.module, seqtype), ]) else: + if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) # bug #22560 putIntoDest(p, d, e, ropecg(p.module, "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim index 5fca784e7ff5..0f8084c78707 100644 --- a/tests/collections/tseq.nim +++ b/tests/collections/tseq.nim @@ -223,3 +223,20 @@ for i in 0..100: var test = newSeqOfCap[uint32](1) test.setLen(1) doAssert test[0] == 0, $(test[0], i) + + +# bug #22560 +doAssert len(newSeqOfCap[int](42)) == 0 + +block: # bug #17197 + type Matrix = seq[seq[int]] + + proc needlemanWunsch(sequence1: string, sequence2: string, gap_penal: int8, match: int8, indel_penal: int8): bool = + let seq2_len = sequence2.len + + var grid: Matrix + for i in sequence1: + grid.add(newSeqOfCap[seq[int]](seq2_len)) + result = true + + doAssert needlemanWunsch("ABC", "DEFG", 1, 2, 3) From 5387b302117bbbc8c52ff2bed416b10a564e8b15 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 31 Aug 2023 22:30:19 +0800 Subject: [PATCH 229/347] closes #22600; adds a test case (#22602) closes #22600 --- tests/statictypes/tstatictypes.nim | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index ac84c4a31c1a..24c99b26bbea 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -411,3 +411,28 @@ block: # Ensure static descriminated objects compile discard instance discard MyObject[KindC]() +block: # bug #22600 + proc f(n: static int): int = n * 2 # same for template + + type + a[N: static int] = object + field : array[N, uint8] + + b[N: static int] = object + field : a[N] + + c[N: static int] = object + f0 : a[N ] # works + f1 : a[N + 1 ] # asserts + f2 : a[f(N) ] # asserts + + f3 : b[N ] # works + f4 : b[N + 1 ] # asserts + f5 : b[f(N) ] # asserts + + proc init[N: static int](x : var a[N]) = discard + proc init[N: static int](x : var b[N]) = discard + proc init[N: static int](x : var c[N]) = x.f1.init() # this is needed + + var x: c[2] + x.init() From b3912c25d3dcb78bc1c8f7d6acc3c512964d3ea8 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 1 Sep 2023 00:01:29 +0800 Subject: [PATCH 230/347] remove outdated config (#22603) --- tools/nimgrep.nim.cfg | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 tools/nimgrep.nim.cfg diff --git a/tools/nimgrep.nim.cfg b/tools/nimgrep.nim.cfg deleted file mode 100644 index 64d3edc7ae79..000000000000 --- a/tools/nimgrep.nim.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# don't use --gc:refc because of bug -# https://github.com/nim-lang/Nim/issues/14138 . -# --gc:orc and --gc:markandsweep work well. ---threads:on --gc:orc From ba158d73dc23fb6e61ebe88f6485f95c8dcb96c2 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Fri, 1 Sep 2023 07:26:53 +0300 Subject: [PATCH 231/347] type annotations for variable tuple unpacking, better error messages (#22611) * type annotations for variable tuple unpacking, better error messages closes #17989, closes https://github.com/nim-lang/RFCs/issues/339 * update grammar * fix test --- compiler/parser.nim | 9 +++++++-- compiler/semexprs.nim | 6 ++++-- compiler/semstmts.nim | 10 ++++++++-- doc/grammar.txt | 2 +- tests/errmsgs/tassignunpack.nim | 2 +- tests/parser/ttupleunpack.nim | 17 +++++++++++++++++ 6 files changed, 38 insertions(+), 8 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index c386df57bf27..7caeca95e15c 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2287,7 +2287,7 @@ proc parseTypeDef(p: var Parser): PNode = setEndInfo() proc parseVarTuple(p: var Parser): PNode = - #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' + #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)? #| varTuple = varTupleLhs '=' optInd expr result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' @@ -2304,9 +2304,14 @@ proc parseVarTuple(p: var Parser): PNode = if p.tok.tokType != tkComma: break getTok(p) skipComment(p, a) - result.add(p.emptyNode) # no type desc optPar(p) eat(p, tkParRi) + if p.tok.tokType == tkColon: + getTok(p) + optInd(p, result) + result.add(parseTypeDesc(p, fullExpr = true)) + else: + result.add(p.emptyNode) # no type desc setEndInfo() proc parseVariable(p: var Parser): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ff9727967fa3..65ed25015ee8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1834,9 +1834,11 @@ proc makeTupleAssignments(c: PContext; n: PNode): PNode = let lhs = n[0] let value = semExprWithType(c, n[1], {efTypeAllowed}) if value.typ.kind != tyTuple: - localError(c.config, n[1].info, errXExpected, "tuple") + localError(c.config, n[1].info, errTupleUnpackingTupleExpected % + [typeToString(value.typ, preferDesc)]) elif lhs.len != value.typ.len: - localError(c.config, n.info, errWrongNumberOfVariables) + localError(c.config, n.info, errTupleUnpackingDifferentLengths % + [$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len]) result = newNodeI(nkStmtList, n.info) let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index bb9c474a8f85..f9168eef631d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -597,14 +597,20 @@ proc globalVarInitCheck(c: PContext, n: PNode) = if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n): localError(c.config, n.info, errCannotAssignToGlobal) +const + errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'" + errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements" + 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 ## ## mirrored with semexprs.makeTupleAssignments if typ.kind != tyTuple: - localError(c.config, a.info, errXExpected, "tuple") + localError(c.config, a.info, errTupleUnpackingTupleExpected % + [typeToString(typ, preferDesc)]) elif a.len-2 != typ.len: - localError(c.config, a.info, errWrongNumberOfVariables) + localError(c.config, a.info, errTupleUnpackingDifferentLengths % + [$(a.len-2), typeToString(typ, preferDesc), $typ.len]) var tempNode: PNode = nil lastDef: PNode diff --git a/doc/grammar.txt b/doc/grammar.txt index 3096eecb52b0..f1484bb0b16c 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -192,7 +192,7 @@ conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue indAndComment? -varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' +varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)? varTuple = varTupleLhs '=' optInd expr colonBody = colcom stmt postExprBlocks? variable = (varTuple / identColonEquals) colonBody? indAndComment diff --git a/tests/errmsgs/tassignunpack.nim b/tests/errmsgs/tassignunpack.nim index d74e16dd5e9f..09d928a54e6c 100644 --- a/tests/errmsgs/tassignunpack.nim +++ b/tests/errmsgs/tassignunpack.nim @@ -1,3 +1,3 @@ var a, b = 0 (a, b) = 1 #[tt.Error - ^ 'tuple' expected]# + ^ tuple expected for tuple unpacking, but got 'int literal(1)']# diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim index 860ef66cfff0..993501fbb0bf 100644 --- a/tests/parser/ttupleunpack.nim +++ b/tests/parser/ttupleunpack.nim @@ -75,3 +75,20 @@ block: # unary assignment unpacking var a: int (a,) = (1,) doAssert a == 1 + +block: # type annotations + block: # basic + let (a, b): (int, int) = (1, 2) + doAssert (a, b) == (1, 2) + block: # type inference + let (a, b): (byte, float) = (1, 2) + doAssert (a, b) == (1.byte, 2.0) + block: # type mismatch + doAssert not (compiles do: + let (a, b): (int, string) = (1, 2)) + block: # nested + let (a, (b, c)): (int, (int, int)) = (1, (2, 3)) + doAssert (a, b, c) == (1, 2, 3) + block: # nested type inference + let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc")) + doAssert (a, b, c) == (1.byte, 2.0, cstring"abc") From 3b206ed988765419c5ff02c2b08645f24ed0cbad Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Fri, 1 Sep 2023 06:41:39 +0200 Subject: [PATCH 232/347] Fix #22604: Make endsInNoReturn traverse the tree (#22612) * Rewrite endsInNoReturn * Handle `try` stmt again and add tests * Fix unreachable code warning * Remove unreachable code in semexprs again * Check `it.len` before skip * Move import of assertions --------- Co-authored-by: SirOlaf <> --- compiler/hlo.nim | 3 -- compiler/sem.nim | 59 +++++++++++++++++++++++++++--- compiler/semexprs.nim | 1 - compiler/semstmts.nim | 4 +- tests/controlflow/tunreachable.nim | 14 ++++++- tests/exprs/t22604.nim | 49 +++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 tests/exprs/t22604.nim diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 744fddcc0f63..9fdec38c0edb 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -10,9 +10,6 @@ # This include implements the high level optimization pass. # included from sem.nim -when defined(nimPreviewSlimSystem): - import std/assertions - proc hlo(c: PContext, n: PNode): PNode proc evalPattern(c: PContext, n, orig: PNode): PNode = diff --git a/compiler/sem.nim b/compiler/sem.nim index 653d83aaa682..7a49def53f96 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -26,7 +26,10 @@ when not defined(leanCompiler): import spawn when defined(nimPreviewSlimSystem): - import std/formatfloat + import std/[ + formatfloat, + assertions, + ] # implementation @@ -207,12 +210,58 @@ proc commonType*(c: PContext; x, y: PType): PType = result.addSonSkipIntLit(r, c.idgen) proc endsInNoReturn(n: PNode): bool = - # check if expr ends in raise exception or call of noreturn proc + ## check if expr ends the block like raising or call of noreturn procs do + result = false # assume it does return + + template checkBranch(branch) = + if not endsInNoReturn(branch): + # proved a branch returns + return false + var it = n - while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: + # skip these beforehand, no special handling needed + while it.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt} and it.len > 0: it = it.lastSon - result = it.kind in nkLastBlockStmts or - it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + + case it.kind + of nkIfStmt: + var hasElse = false + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `if` statement during endsInNoReturn" + # none of the branches returned + result = hasElse # Only truly a no-return when it's exhaustive + of nkCaseStmt: + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + branch[0] + else: + raiseAssert "Malformed `case` statement in endsInNoReturn" + # none of the branches returned + result = true + of nkTryStmt: + checkBranch(it[0]) + for i in 1 ..< it.len: + let branch = it[i] + checkBranch(branch[^1]) + # none of the branches returned + result = true + else: + result = it.kind in nkLastBlockStmts or + it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags proc commonType*(c: PContext; x: PType, y: PNode): PType = # ignore exception raising branches in case/if expressions diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 65ed25015ee8..f612cd9968ee 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1113,7 +1113,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType msg.addDeclaredLocMaybe(c.config, typ) localError(c.config, n.info, msg) return errorNode(c, n) - result = nil else: result = m.call instGenericConvertersSons(c, result, m) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f9168eef631d..ee1b56fed4cb 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2651,9 +2651,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType = var m = n[i] while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates m = m.lastSon - if m.kind in nkLastBlockStmts or - m.kind in nkCallKinds and m[0].kind == nkSym and - sfNoReturn in m[0].sym.flags: + if endsInNoReturn(m): for j in i + 1..<n.len: case n[j].kind of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard diff --git a/tests/controlflow/tunreachable.nim b/tests/controlflow/tunreachable.nim index 11c8595ebf35..64e199e17f01 100644 --- a/tests/controlflow/tunreachable.nim +++ b/tests/controlflow/tunreachable.nim @@ -2,8 +2,9 @@ discard """ cmd: "nim check --warningAsError:UnreachableCode $file" action: "reject" nimout: ''' -tunreachable.nim(23, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] -tunreachable.nim(30, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] +tunreachable.nim(24, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] +tunreachable.nim(31, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] +tunreachable.nim(40, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode] ''' """ @@ -30,3 +31,12 @@ proc main2() = echo "after" main2() + +proc main3() = + if true: + return + else: + return + echo "after" + +main3() \ No newline at end of file diff --git a/tests/exprs/t22604.nim b/tests/exprs/t22604.nim new file mode 100644 index 000000000000..570f989d64ae --- /dev/null +++ b/tests/exprs/t22604.nim @@ -0,0 +1,49 @@ +# if +for i in 0..<1: + let x = + case false + of true: + 42 + of false: + if true: + continue + else: + raiseAssert "Won't get here" + +# block +for i in 0..<1: + let x = + case false + of true: + 42 + of false: + block: + if true: + continue + else: + raiseAssert "Won't get here" + +# nested case +for i in 0..<1: + let x = + case false + of true: + 42 + of false: + case true + of true: + continue + of false: + raiseAssert "Won't get here" + +# try except +for i in 0..<1: + let x = + case false + of true: + 42 + of false: + try: + continue + except: + raiseAssert "Won't get here" \ No newline at end of file From affd3f78587f1b18a8b4bc2fc51967cd55cb7531 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:55:19 +0800 Subject: [PATCH 233/347] fixes #22613; Default value does not work with object's discriminator (#22614) * fixes #22613; Default value does not work with object's discriminator fixes #22613 * merge branches * add a test case * fixes status * remove outdated comments * move collectBranchFields into the global scope --- compiler/semobjconstr.nim | 66 ++++++++++++++++--------- tests/objects/tobject_default_value.nim | 17 ++++++- 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 3e0a6fdb704b..2d366d8fcb9e 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -75,7 +75,6 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = proc semConstrField(c: PContext, flags: TExprFlags, field: PSym, initExpr: PNode): PNode = - result = nil let assignment = locateFieldInInitExpr(c, field, initExpr) if assignment != nil: if nfSem in assignment.flags: return assignment[1] @@ -93,7 +92,9 @@ proc semConstrField(c: PContext, flags: TExprFlags, assignment[0] = newSymNode(field) assignment[1] = initValue assignment.flags.incl nfSem - return initValue + result = initValue + else: + result = nil proc branchVals(c: PContext, caseNode: PNode, caseIdx: int, isStmtBranch: bool): IntSet = @@ -192,6 +193,23 @@ proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode, asgnExpr.typ = recTyp defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr) +proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode, + constrCtx: var ObjConstrContext, flags: TExprFlags) = + # All bets are off. If any of the branches has a mandatory + # fields we must produce an error: + for i in 1..<n.len: + let branchNode = n[i] + if branchNode != nil: + let oldCheckDefault = constrCtx.checkDefault + constrCtx.checkDefault = true + let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags) + constrCtx.checkDefault = oldCheckDefault + if len(defaults) > 0: + localError(c.config, discriminatorVal.info, "branch initialization " & + "with a runtime discriminator is not supported " & + "for a branch whose fields have default values.") + discard collectMissingCaseFields(c, n[i], constrCtx, @[]) + proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] = result = (initUnknown, @[]) @@ -331,13 +349,27 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, discriminator.sym, constrCtx.initExpr) if discriminatorVal == nil: - # None of the branches were explicitly selected by the user and no - # value was given to the discrimator. We can assume that it will be - # initialized to zero and this will select a particular branch as - # a result: - let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) - let matchedBranch = n.pickCaseBranch defaultValue - discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[]) + if discriminator.sym.ast != nil: + # branch is selected by the default field value of discriminator + let discriminatorDefaultVal = discriminator.sym.ast + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal) + if discriminatorDefaultVal.kind == nkIntLit: + let matchedBranch = n.pickCaseBranch discriminatorDefaultVal + if matchedBranch != nil: + let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags) + result.defaults.add defaults + collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) + else: + collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags) + else: + # None of the branches were explicitly selected by the user and no + # value was given to the discrimator. We can assume that it will be + # initialized to zero and this will select a particular branch as + # a result: + let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) + let matchedBranch = n.pickCaseBranch defaultValue + discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[]) else: result.status = initPartial if discriminatorVal.kind == nkIntLit: @@ -349,20 +381,8 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, result.defaults.add defaults collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) else: - # All bets are off. If any of the branches has a mandatory - # fields we must produce an error: - for i in 1..<n.len: - let branchNode = n[i] - if branchNode != nil: - let oldCheckDefault = constrCtx.checkDefault - constrCtx.checkDefault = true - let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags) - constrCtx.checkDefault = oldCheckDefault - if len(defaults) > 0: - localError(c.config, discriminatorVal.info, "branch initialization " & - "with a runtime discriminator is not supported " & - "for a branch whose fields have default values.") - discard collectMissingCaseFields(c, n[i], constrCtx, @[]) + collectBranchFields(c, n, discriminatorVal, constrCtx, flags) + of nkSym: let field = n.sym let e = semConstrField(c, flags, field, constrCtx.initExpr) diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim index 2d86dce1154e..3af790da6a4e 100644 --- a/tests/objects/tobject_default_value.nim +++ b/tests/objects/tobject_default_value.nim @@ -377,7 +377,7 @@ template main {.dirty.} = Red, Blue, Yellow type - ObjectVarint3 = object # fixme it doesn't work with static + ObjectVarint3 = object case kind: Color = Blue of Red: data1: int = 10 @@ -703,5 +703,20 @@ template main {.dirty.} = var foo = new Container doAssert int(foo.thing[0].x) == 1 + block: # bug #22613 + type + K = enum + A = "a" + B = "b" + T = object + case kind: K = B + of A: + a: int + of B: + b: float + + doAssert T().kind == B + + static: main() main() From 53d9fb259fc7d36660be20be0a199625ef126376 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Fri, 1 Sep 2023 09:59:48 +0300 Subject: [PATCH 234/347] don't update const symbol on const section re-sems (#22609) fixes #19849 --- compiler/semstmts.nim | 15 ++++++++++----- tests/vm/tconstresem.nim | 10 ++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 tests/vm/tconstresem.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ee1b56fed4cb..08d6d44e6e8c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -867,9 +867,13 @@ proc semConst(c: PContext, n: PNode): PNode = styleCheckDef(c, v) onDef(a[j].info, v) - setVarType(c, v, typ) - when false: - v.ast = def # no need to copy + var fillSymbol = true + if v.typ != nil: + # symbol already has type and probably value + # don't mutate + fillSymbol = false + else: + setVarType(c, v, typ) b = newNodeI(nkConstDef, a.info) if importantComments(c.config): b.comment = a.comment # postfix not generated here (to generate, get rid of it in transf) @@ -882,8 +886,9 @@ proc semConst(c: PContext, n: PNode): PNode = b.add newSymNode(v) b.add a[1] b.add copyTree(def) - v.ast = b - addToVarSection(c, result, n, b) + if fillSymbol: + v.ast = b + addToVarSection(c, result, n, b) dec c.inStaticContext include semfields diff --git a/tests/vm/tconstresem.nim b/tests/vm/tconstresem.nim new file mode 100644 index 000000000000..4526cb891d56 --- /dev/null +++ b/tests/vm/tconstresem.nim @@ -0,0 +1,10 @@ +block: # issue #19849 + type + Vec2[T] = object + x, y: T + Vec2i = Vec2[int] + template getX(p: Vec2i): int = p.x + let x = getX: + const t = Vec2i(x: 1, y: 2) + t + doAssert x == 1 From f1789cc465bcabc7afe0fe991df615246cfadb3b Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Fri, 1 Sep 2023 10:00:15 +0300 Subject: [PATCH 235/347] resolve local symbols in generic type call RHS (#22610) resolve local symbols in generic type call fixes #14509 --- compiler/semtypes.nim | 1 + tests/generics/m14509.nim | 16 ++++++++++++++++ tests/generics/t14509.nim | 4 ++++ 3 files changed, 21 insertions(+) create mode 100644 tests/generics/m14509.nim create mode 100644 tests/generics/t14509.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8e304288b6d1..b469c69fbfb7 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1995,6 +1995,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semTypeExpr(c, n[1], prev) else: if c.inGenericContext > 0 and n.kind == nkCall: + let n = semGenericStmt(c, n) result = makeTypeFromExpr(c, n.copyTree) else: result = semTypeExpr(c, n, prev) diff --git a/tests/generics/m14509.nim b/tests/generics/m14509.nim new file mode 100644 index 000000000000..cabc4f308b74 --- /dev/null +++ b/tests/generics/m14509.nim @@ -0,0 +1,16 @@ +import macros + +type float32x4 = array[4, float32] +type float32x8 = array[8, float32] + +{.experimental: "dynamicBindSym".} +macro dispatch(N: static int, T: type SomeNumber): untyped = + let BaseT = getTypeInst(T)[1] + result = bindSym($BaseT & "x" & $N) + +type + VecIntrin*[N: static int, T: SomeNumber] = dispatch(N, T) + +func `$`*[N, T](vec: VecIntrin[N, T]): string = + ## Display a vector + $cast[array[N, T]](vec) diff --git a/tests/generics/t14509.nim b/tests/generics/t14509.nim new file mode 100644 index 000000000000..ef3143ee4f1b --- /dev/null +++ b/tests/generics/t14509.nim @@ -0,0 +1,4 @@ +import m14509 + +var v: VecIntrin[4, float32] +doAssert $v == "[0.0, 0.0, 0.0, 0.0]" From 0c6e13806d0abfad30b8a4bd9f1fe7858ff2125a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Fri, 1 Sep 2023 12:42:47 +0100 Subject: [PATCH 236/347] fixes internal error: no generic body fixes #1500 (#22580) * fixes internal error: no generic body fixes #1500 * adds guard * adds guard * removes unnecessary test * refactor: extracts containsGenericInvocationWithForward --- compiler/semdata.nim | 2 +- compiler/semtypes.nim | 10 ++++++++++ tests/generics/t1500.nim | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/generics/t1500.nim diff --git a/compiler/semdata.nim b/compiler/semdata.nim index db3b8370ee56..00559a5b6142 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -168,7 +168,7 @@ type sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index inUncheckedAssignSection*: int importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id]) - skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance and sets. + skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies. TBorrowState* = enum bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b469c69fbfb7..282bc53fea6a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1508,6 +1508,14 @@ proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): b var newf = newNodeI(nkRecList, n.info) semRecordNodeAux(c, t.n, check, pos, newf, t) +proc containsGenericInvocationWithForward(n: PNode): bool = + if n.kind == nkSym and n.sym.ast != nil and n.sym.ast.len > 1 and n.sym.ast[2].kind == nkObjectTy: + for p in n.sym.ast[2][^1]: + if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and + p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward: + return true + return false + proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(c.config, n.info, "cannot instantiate the '$1' $2" % @@ -1577,6 +1585,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = # XXX: What kind of error is this? is it still relevant? localError(c.config, n.info, errCannotInstantiateX % s.name.s) result = newOrPrevType(tyError, prev, c) + elif containsGenericInvocationWithForward(n[0]): + c.skipTypes.add n #fixes 1500 else: result = instGenericContainer(c, n.info, result, allowMetaTypes = false) diff --git a/tests/generics/t1500.nim b/tests/generics/t1500.nim new file mode 100644 index 000000000000..6dd457d336c0 --- /dev/null +++ b/tests/generics/t1500.nim @@ -0,0 +1,8 @@ +#issue 1500 + +type + TFtpBase*[SockType] = object + job: TFTPJob[SockType] + PFtpBase*[SockType] = ref TFtpBase[SockType] + TFtpClient* = TFtpBase[string] + TFTPJob[T] = object \ No newline at end of file From 6738f44af3866e4cace94d34b653acf3283f0cde Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Fri, 1 Sep 2023 16:37:16 +0300 Subject: [PATCH 237/347] unify explicit generic param semchecking in calls (#22618) fixes #9040 --- compiler/semcall.nim | 8 ++++++-- compiler/semexprs.nim | 4 ---- tests/generics/timplicit_and_explicit.nim | 21 ++++++++++++++++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index adfc98e1973d..a4114497fb98 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -733,14 +733,18 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = onUse(info, s) result = newSymNode(newInst, info) -proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = - assert n.kind == nkBracketExpr +proc setGenericParams(c: PContext, n: PNode) = + ## sems generic params in subscript expression for i in 1..<n.len: let e = semExprWithType(c, n[i]) if e.typ == nil: n[i].typ = errorType(c) else: n[i].typ = e.typ.skipTypes({tyTypeDesc}) + +proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = + assert n.kind == nkBracketExpr + setGenericParams(c, n) var s = s var a = n[0] if a.kind == nkSym: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f612cd9968ee..e9f90dcc05d3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1003,10 +1003,6 @@ proc bracketedMacro(n: PNode): PSym = else: result = nil -proc setGenericParams(c: PContext, n: PNode) = - for i in 1..<n.len: - n[i].typ = semTypeNode(c, n[i], nil) - proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError: return errorNode(c, n) diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim index ba24b79e9c3f..fe61004e4cb4 100644 --- a/tests/generics/timplicit_and_explicit.nim +++ b/tests/generics/timplicit_and_explicit.nim @@ -42,4 +42,23 @@ block: #4688 block: #4164 proc printStr[T](s: static[string]): T = discard - discard printStr[int]("hello static") \ No newline at end of file + discard printStr[int]("hello static") + +import macros + +block: # issue #9040, statics with template, macro, symchoice explicit generics + block: # macro + macro fun[N: static int](): untyped = + newLit 1 + const a = fun[2]() + doAssert a == 1 + block: # template + template fun[N: static int](): untyped = + 1 + const a = fun[2]() + doAssert a == 1 + block: # symchoice + proc newSeq[x: static int](): int = 1 + template foo: int = + newSeq[2]() + doAssert foo() == 1 From 2542dc09c829a709050335066b0f414d6fc68fe2 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Fri, 1 Sep 2023 16:38:25 +0300 Subject: [PATCH 238/347] use dummy dest for void branches to fix noreturn in VM (#22617) fixes #22216 --- compiler/vmgen.nim | 39 ++++++++++++++++++++++++++++++--------- tests/vm/tnoreturn.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 tests/vm/tnoreturn.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 1f51b26cc264..33e1d8463f7d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -408,13 +408,19 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) = c.gen(it[0], tmp) elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false c.clearDest(n, dest) - c.gen(it[1], dest) # then part + if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `dest` + c.gen(it[1]) + else: + c.gen(it[1], dest) # then part if i < n.len-1: endings.add(c.xjmp(it[1], opcJmp, 0)) c.patch(elsePos) else: c.clearDest(n, dest) - c.gen(it[0], dest) + if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `dest` + c.gen(it[0]) + else: + c.gen(it[0], dest) for endPos in endings: c.patch(endPos) c.clearDest(n, dest) @@ -508,17 +514,25 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = let it = n[i] if it.len == 1: # else stmt: - if it[0].kind != nkNilLit or it[0].typ != nil: + let body = it[0] + if body.kind != nkNilLit or body.typ != nil: # an nkNilLit with nil for typ implies there is no else branch, this # avoids unused related errors as we've already consumed the dest - c.gen(it[0], dest) + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) else: let b = rawGenLiteral(c, it) c.gABx(it, opcBranch, tmp, b) - let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp) - c.gen(it.lastSon, dest) + let body = it.lastSon + let elsePos = c.xjmp(body, opcFJmp, tmp) + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) if i < n.len-1: - endings.add(c.xjmp(it.lastSon, opcJmp, 0)) + endings.add(c.xjmp(body, opcJmp, 0)) c.patch(elsePos) c.clearDest(n, dest) for endPos in endings: c.patch(endPos) @@ -534,7 +548,10 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) var endings: seq[TPosition] = @[] let ehPos = c.xjmp(n, opcTry, 0) - c.gen(n[0], dest) + if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `dest` + c.gen(n[0]) + else: + c.gen(n[0], dest) c.clearDest(n, dest) # Add a jump past the exception handling code let jumpToFinally = c.xjmp(n, opcJmp, 0) @@ -552,7 +569,11 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = if it.len == 1: # general except section: c.gABx(it, opcExcept, 0, 0) - c.gen(it.lastSon, dest) + let body = it.lastSon + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) c.clearDest(n, dest) if i < n.len: endings.add(c.xjmp(it, opcJmp, 0)) diff --git a/tests/vm/tnoreturn.nim b/tests/vm/tnoreturn.nim new file mode 100644 index 000000000000..d4f8601a9017 --- /dev/null +++ b/tests/vm/tnoreturn.nim @@ -0,0 +1,32 @@ +block: # issue #22216 + type + Result[T, E] = object + case oVal: bool + of false: + eVal: E + of true: + vVal: T + + func raiseResultDefect(m: string) {.noreturn, noinline.} = + raise (ref Defect)(msg: m) + + template withAssertOk(self: Result, body: untyped): untyped = + case self.oVal + of false: + raiseResultDefect("Trying to access value with err Result") + else: + body + + func value[T, E](self: Result[T, E]): T {.inline.} = + withAssertOk(self): + self.vVal + + const + x = Result[int, string](oVal: true, vVal: 123) + z = x.value() + + let + xx = Result[int, string](oVal: true, vVal: 123) + zz = x.value() + + doAssert z == zz From 9f1fe8a2da27abc1e93a05debbb2622b524aae0d Mon Sep 17 00:00:00 2001 From: Pylgos <43234674+Pylgos@users.noreply.github.com> Date: Sat, 2 Sep 2023 13:00:26 +0900 Subject: [PATCH 239/347] Fix the problem where instances of generic objects with `sendable` pragmas are not being cached (#22622) remove `tfSendable` from `eqTypeFlags` --- compiler/ast.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index ce5ad0f292a0..e82a9a293417 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -641,7 +641,7 @@ const skError* = skUnknown var - eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam, tfSendable} + eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam} ## type flags that are essential for type equality. ## This is now a variable because for emulation of version:1.0 we ## might exclude {tfGcSafe, tfNoSideEffect}. From bd6adbcc9d5a22f686eed7bc988a1c0b1b0a17e4 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Sat, 2 Sep 2023 11:32:46 +0300 Subject: [PATCH 240/347] fix isNil folding for compile time closures (#22574) fixes #20543 --- compiler/semfold.nim | 8 +++++++- tests/vm/tvmmisc.nim | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index c6dec09a9ba7..f1a1c8a82f66 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -241,7 +241,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g) of mDivF64: result = newFloatNodeT(getFloat(a) / getFloat(b), n, g) - of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, idgen, g) + of mIsNil: + let val = a.kind == nkNilLit or + # nil closures have the value (nil, nil) + (a.typ != nil and skipTypes(a.typ, abstractRange).kind == tyProc and + a.kind == nkTupleConstr and a.len == 2 and + a[0].kind == nkNilLit and a[1].kind == nkNilLit) + result = newIntNodeT(toInt128(ord(val)), n, idgen, g) of mLtI, mLtB, mLtEnum, mLtCh: result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g) of mLeI, mLeB, mLeEnum, mLeCh: diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 1429ef6e97e3..cade68577cd9 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -232,6 +232,14 @@ block: # bug #15595 static: main() main() +block: # issue #20543 + type F = proc() + const myArray = block: + var r: array[1, F] + r[0] = nil + r + doAssert isNil(myArray[0]) + # bug #15363 import sequtils From d2f36c071b1967e864961f423596d3931e84e49b Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sat, 2 Sep 2023 20:42:40 +0200 Subject: [PATCH 241/347] Exclude block from endsInNoReturn, fix regression (#22632) Co-authored-by: SirOlaf <> --- compiler/sem.nim | 2 +- tests/exprs/t22604.nim | 13 ------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 7a49def53f96..1a080f869f13 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -220,7 +220,7 @@ proc endsInNoReturn(n: PNode): bool = var it = n # skip these beforehand, no special handling needed - while it.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt} and it.len > 0: + while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: it = it.lastSon case it.kind diff --git a/tests/exprs/t22604.nim b/tests/exprs/t22604.nim index 570f989d64ae..c41cd3dfa251 100644 --- a/tests/exprs/t22604.nim +++ b/tests/exprs/t22604.nim @@ -10,19 +10,6 @@ for i in 0..<1: else: raiseAssert "Won't get here" -# block -for i in 0..<1: - let x = - case false - of true: - 42 - of false: - block: - if true: - continue - else: - raiseAssert "Won't get here" - # nested case for i in 0..<1: let x = From 480e98c479035a8a19ff543bace3616d202e1ea2 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Sun, 3 Sep 2023 14:59:03 +0300 Subject: [PATCH 242/347] resolve unambiguous enum symchoices from local scope, error on rest (#22606) fixes #22598, properly fixes #21887 and fixes test case issue number When an enum field sym choice has to choose a type, check if its name is ambiguous in the local scope, then check if the first symbol found in the local scope is the first symbol in the sym choice. If so, choose that symbol. Otherwise, give an ambiguous identifier error. The dependence on the local scope implies this will always give ambiguity errors for unpicked enum symchoices from generics and templates and macros from other scopes. We can change `not isAmbiguous(...) and foundSym == first` to `not (isAmbiguous(...) and foundSym == first)` to make it so they never give ambiguity errors, and always pick the first symbol in the symchoice. I can do this if this is preferred, but no code from CI seems affected. --- compiler/lookups.nim | 35 ++++++++++++++++++++++++++++++ compiler/semexprs.nim | 5 ++++- tests/enum/tambiguousoverloads.nim | 26 ++++++++++++++++++++++ tests/enum/toverloadable_enums.nim | 8 ------- tests/lookups/tambsym3.nim | 7 ++---- 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 tests/enum/tambiguousoverloads.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 3cf759981fa8..90f9a9b2b472 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -254,6 +254,41 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy if s.kind in filter: result.add s +proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool = + result = false + block outer: + for scope in allScopes(c.currentScope): + var ti: TIdentIter + var candidate = initIdentIter(ti, scope.symbols, s) + var scopeHasCandidate = false + while candidate != nil: + if candidate.kind in filter: + if scopeHasCandidate: + # 2 candidates in same scope, ambiguous + return true + else: + scopeHasCandidate = true + sym = candidate + candidate = nextIdentIter(ti, scope.symbols) + if scopeHasCandidate: + # scope had a candidate but wasn't ambiguous + return false + + var importsHaveCandidate = false + var marked = initIntSet() + for im in c.imports.mitems: + for s in symbols(im, marked, s, c.graph): + if s.kind in filter: + if importsHaveCandidate: + # 2 candidates among imports, ambiguous + return true + else: + importsHaveCandidate = true + sym = s + if importsHaveCandidate: + # imports had a candidate but wasn't ambiguous + return false + proc errorSym*(c: PContext, n: PNode): PSym = ## creates an error symbol to avoid cascading errors (for IDE support) var m = n diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e9f90dcc05d3..001ce958a80c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -92,7 +92,10 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode = let first = n[0].sym - if first.kind == skEnumField: + var foundSym: PSym = nil + if first.kind == skEnumField and + not isAmbiguous(c, first.name, {skEnumField}, foundSym) and + foundSym == first: # choose the first resolved enum field, i.e. the latest in scope # to mirror behavior before overloadable enums if hintAmbiguousEnum in c.config.notes: diff --git a/tests/enum/tambiguousoverloads.nim b/tests/enum/tambiguousoverloads.nim new file mode 100644 index 000000000000..1b0d92608585 --- /dev/null +++ b/tests/enum/tambiguousoverloads.nim @@ -0,0 +1,26 @@ +discard """ +cmd: "nim check --hints:off $file" +""" + +block: # bug #21887 + type + EnumA = enum A = 300, B + EnumB = enum A = 10 + EnumC = enum C + + doAssert typeof(EnumC(A)) is EnumC #[tt.Error + ^ ambiguous identifier 'A' -- use one of the following: + EnumA.A: EnumA + EnumB.A: EnumB]# + +block: # issue #22598 + type + A = enum + red + B = enum + red + + let a = red #[tt.Error + ^ ambiguous identifier 'red' -- use one of the following: + A.red: A + B.red: B]# diff --git a/tests/enum/toverloadable_enums.nim b/tests/enum/toverloadable_enums.nim index 5fdcb1823805..9bb5514674ca 100644 --- a/tests/enum/toverloadable_enums.nim +++ b/tests/enum/toverloadable_enums.nim @@ -118,11 +118,3 @@ block: # test with macros/templates doAssert isOneMS(e2) doAssert isOneT(e1) doAssert isOneT(e2) - -block: # bug #21908 - type - EnumA = enum A = 300, B - EnumB = enum A = 10 - EnumC = enum C - - doAssert typeof(EnumC(A)) is EnumC diff --git a/tests/lookups/tambsym3.nim b/tests/lookups/tambsym3.nim index e50f9c4618a3..6e7589cd8a6d 100644 --- a/tests/lookups/tambsym3.nim +++ b/tests/lookups/tambsym3.nim @@ -1,15 +1,12 @@ discard """ - errormsg: "ambiguous enum field" + errormsg: "ambiguous identifier 'mDec' -- use one of the following:" file: "tambsym3.nim" - line: 14 + line: 11 """ # Test ambiguous symbols import mambsym1, times -{.hint[AmbiguousEnum]: on.} -{.hintAsError[AmbiguousEnum]: on.} - var v = mDec #ERROR_MSG ambiguous identifier From c5495f40d5d881e6bd155c9e6c9c6e5e49b749a7 Mon Sep 17 00:00:00 2001 From: Andrey Makarov <ph.makarov@gmail.com> Date: Sun, 3 Sep 2023 08:09:36 -0600 Subject: [PATCH 243/347] docgen: add Pandoc footnotes (fixes #21080) (#22591) This implements Pandoc Markdown-style footnotes, that are compatible with Pandoc referencing syntax: Ref. [^ftn]. [^ftn]: Block. See https://pandoc.org/MANUAL.html#footnotes for more examples. --- doc/contributing.md | 4 +- doc/docgen.md | 4 +- doc/markdown_rst.md | 20 +-- lib/packages/docutils/rst.nim | 221 +++++++++++++++++++++------------- tests/stdlib/trst.nim | 67 +++++++++++ tests/stdlib/trstgen.nim | 6 +- 6 files changed, 221 insertions(+), 101 deletions(-) diff --git a/doc/contributing.md b/doc/contributing.md index 47e1fa3dd647..420c1438e9a8 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -336,7 +336,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files: .. Note:: ``*.rst`` files have ``:literal:`` as their default role. So for them the rule above is only applicable if the ``:nim:`` role - is set up manually as the default \[*]: + is set up manually as the default [^1]: .. role:: nim(code) :language: nim @@ -345,7 +345,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files: The first 2 lines are for other RST implementations, including Github one. - \[*] this is fulfilled when ``doc/rstcommon.rst`` is included. + [^1]: this is fulfilled when ``doc/rstcommon.rst`` is included. Best practices ============== diff --git a/doc/docgen.md b/doc/docgen.md index a05da8198f8a..21058e88d55f 100644 --- a/doc/docgen.md +++ b/doc/docgen.md @@ -354,9 +354,9 @@ This pertains to any exported symbol like `proc`, `const`, `iterator`, etc. Link text is either one word or a group of words enclosed by delimiters (brackets ``[...]`` for Markdown or backticks `\`...\`_` for RST). Link text will be displayed *as is* while *link target* will be set to -the anchor \[*] of Nim symbol that corresponds to link text. +the anchor [^1] of Nim symbol that corresponds to link text. -\[*] anchors' format is described in [HTML anchor generation] section below. +[^1] anchors' format is described in [HTML anchor generation] section below. If you have a constant: diff --git a/doc/markdown_rst.md b/doc/markdown_rst.md index 9a266be9a067..b7f0916494b6 100644 --- a/doc/markdown_rst.md +++ b/doc/markdown_rst.md @@ -32,12 +32,12 @@ The `md2tex`:option: command is invoked identically to `md2html`:option:, but outputs a ``.tex`` file instead of ``.html``. These tools embedded into Nim compiler; the compiler can output -the result to HTML \[#html] or Latex \[#latex]. +the result to HTML [^html] or Latex [^latex]. -\[#html] commands `nim doc`:cmd: for ``*.nim`` files and +[^html]: commands `nim doc`:cmd: for ``*.nim`` files and `nim rst2html`:cmd: for ``*.rst`` files -\[#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and +[^latex]: commands `nim doc2tex`:cmd: for ``*.nim`` and `nim rst2tex`:cmd: for ``*.rst``. Full list of supported commands: @@ -127,7 +127,9 @@ Markdown-specific features ``` * Markdown links ``[...](...)`` * Pandoc syntax for automatic links ``[...]``, see [Referencing] for description -+ Markdown literal blocks indented by 4 or more spaces +* Pandoc syntax for footnotes, including ``[^10]`` (manually numbered) + and ``[^someName]`` (auto-numbered with a label) +* Markdown literal blocks indented by 4 or more spaces * Markdown headlines * Markdown block quotes * Markdown syntax for definition lists @@ -139,8 +141,8 @@ Additional Nim-specific features * referencing to definitions in external files, see [Markup external referencing] section -* directives: ``code-block`` \[cmp:Sphinx], ``title``, - ``index`` \[cmp:Sphinx] +* directives: ``code-block`` [^Sphinx], ``title``, + ``index`` [^Sphinx] * predefined roles - ``:nim:`` (default), ``:c:`` (C programming language), ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). @@ -154,9 +156,9 @@ Additional Nim-specific features - ``:cmd:`` for commands and common shells syntax - ``:console:`` the same for interactive sessions (commands should be prepended by ``$``) - - ``:program:`` for executable names \[cmp:Sphinx] + - ``:program:`` for executable names [^Sphinx] (one can just use ``:cmd:`` on single word) - - ``:option:`` for command line options \[cmp:Sphinx] + - ``:option:`` for command line options [^Sphinx] - ``:tok:``, a role for highlighting of programming language tokens * ***triple emphasis*** (bold and italic) using \*\*\* * ``:idx:`` role for \`interpreted text\` to include the link to this @@ -171,7 +173,7 @@ Additional Nim-specific features and `doc`:option: will be left in the final document. * emoji / smiley symbols -\[cmp:Sphinx] similar but different from the directives of +[^Sphinx]: similar but different from the directives of Python [Sphinx directives] and [Sphinx roles] extensions .. Note:: By default Nim has ``roSupportMarkdown`` and diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 2894010ef5f0..a9bc4db9164a 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -442,6 +442,7 @@ type ## because RST can have >1 alias per 1 anchor EParseError* = object of ValueError + SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.} const LineRstInit* = 1 ## Initial line number for standalone RST text @@ -597,8 +598,10 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) = # # TODO: we need to apply this strategy to all markup elements eventually. -func isPureRst(p: RstParser): bool = - roSupportMarkdown notin p.s.options +func isPureRst(p: RstParser): bool = roSupportMarkdown notin p.s.options +func isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options +func isMd(p: RstParser): bool = roPreferMarkdown in p.s.options +func isMd(s: PRstSharedState): bool = roPreferMarkdown in s.options proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string) = let realMsgKind = if isPureRst(p): errorType else: mwRstStyle @@ -1692,7 +1695,7 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = else: result = false -proc getFootnoteType(label: PRstNode): (FootnoteType, int) = +proc getRstFootnoteType(label: PRstNode): (FootnoteType, int) = if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and label.sons[0].text == "#": if label.sons.len == 1: @@ -1710,7 +1713,18 @@ proc getFootnoteType(label: PRstNode): (FootnoteType, int) = else: result = (fnCitation, -1) -proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = +proc getMdFootnoteType(label: PRstNode): (FootnoteType, int) = + try: + result = (fnManualNumber, parseInt(label.sons[0].text)) + except ValueError: + result = (fnAutoNumberLabel, -1) + +proc getFootnoteType(s: PRstSharedState, label: PRstNode): (FootnoteType, int) = + ## Returns footnote/citation type and manual number (if present). + if isMd(s): getMdFootnoteType(label) + else: getRstFootnoteType(label) + +proc parseRstFootnoteName(p: var RstParser, reference: bool): PRstNode = ## parse footnote/citation label. Precondition: start at `[`. ## Label text should be valid ref. name symbol, otherwise nil is returned. var i = p.idx + 1 @@ -1740,6 +1754,41 @@ proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = inc i p.idx = i +proc isMdFootnoteName(p: RstParser, reference: bool): bool = + ## Pandoc Markdown footnote extension. + let j = p.idx + result = p.tok[j].symbol == "[" and p.tok[j+1].symbol == "^" and + p.tok[j+2].kind == tkWord + +proc parseMdFootnoteName(p: var RstParser, reference: bool): PRstNode = + if isMdFootnoteName(p, reference): + result = newRstNode(rnInner) + var j = p.idx + 2 + while p.tok[j].kind in {tkWord, tkOther} or + validRefnamePunct(p.tok[j].symbol): + result.add newLeaf(p.tok[j].symbol) + inc j + if j == p.idx + 2: + return nil + if p.tok[j].symbol == "]": + if reference: + p.idx = j + 1 # skip ] + else: + if p.tok[j+1].symbol == ":": + p.idx = j + 2 # skip ]: + else: + result = nil + else: + result = nil + else: + result = nil + +proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = + if isMd(p): parseMdFootnoteName(p, reference) + else: + if isInlineMarkupStart(p, "["): parseRstFootnoteName(p, reference) + else: nil + proc isMarkdownCodeBlock(p: RstParser, idx: int): bool = let tok = p.tok[idx] template allowedSymbol: bool = @@ -1806,16 +1855,12 @@ proc parseInline(p: var RstParser, father: PRstNode) = var n = newRstNode(rnSubstitutionReferences, info=lineInfo(p, p.idx+1)) parseUntil(p, n, "|", false) father.add(n) - elif roSupportMarkdown in p.s.options and - currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and - parseMarkdownLink(p, father): - discard "parseMarkdownLink already processed it" - elif isInlineMarkupStart(p, "[") and nextTok(p).symbol != "[" and + elif currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and (n = parseFootnoteName(p, reference=true); n != nil): var nn = newRstNode(rnFootnoteRef) nn.info = lineInfo(p, saveIdx+1) nn.add n - let (fnType, _) = getFootnoteType(n) + let (fnType, _) = getFootnoteType(p.s, n) case fnType of fnAutoSymbol: p.s.lineFootnoteSymRef.add lineInfo(p) @@ -1823,6 +1868,10 @@ proc parseInline(p: var RstParser, father: PRstNode) = p.s.lineFootnoteNumRef.add lineInfo(p) else: discard father.add(nn) + elif roSupportMarkdown in p.s.options and + currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and + parseMarkdownLink(p, father): + discard "parseMarkdownLink already processed it" else: if roSupportSmilies in p.s.options: let n = parseSmiley(p) @@ -1960,8 +2009,26 @@ proc getMdBlockIndent(p: RstParser): int = else: result = nextIndent # allow parsing next lines [case.3] -template isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options -template isMd(p: RstParser): bool = roPreferMarkdown in p.s.options +proc indFollows(p: RstParser): bool = + result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) + +proc parseBlockContent(p: var RstParser, father: var PRstNode, + contentParser: SectionParser): bool {.gcsafe.} = + ## parse the final content part of explicit markup blocks (directives, + ## footnotes, etc). Returns true if succeeded. + if currentTok(p).kind != tkIndent or indFollows(p): + let blockIndent = getWrappableIndent(p) + pushInd(p, blockIndent) + let content = contentParser(p) + popInd(p) + father.add content + result = true + +proc parseSectionWrapper(p: var RstParser): PRstNode = + result = newRstNode(rnInner) + parseSection(p, result) + while result.kind == rnInner and result.len == 1: + result = result.sons[0] proc parseField(p: var RstParser): PRstNode = ## Returns a parsed rnField node. @@ -2298,6 +2365,8 @@ proc whichSection(p: RstParser): RstNodeKind = elif roSupportMarkdown in p.s.options and predNL(p) and match(p, p.idx, "| w") and findPipe(p, p.idx+3): result = rnMarkdownTable + elif isMd(p) and isMdFootnoteName(p, reference=false): + result = rnFootnote elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p): @@ -2866,7 +2935,7 @@ proc parseOptionList(p: var RstParser): PRstNode = break proc parseMdDefinitionList(p: var RstParser): PRstNode = - ## Parses (Pandoc/kramdown/PHPextra) Mardkown definition lists. + ## Parses (Pandoc/kramdown/PHPextra) Markdown definition lists. result = newRstNodeA(p, rnMdDefList) let termCol = currentTok(p).col while true: @@ -3022,6 +3091,57 @@ proc parseEnumList(p: var RstParser): PRstNode = else: break +proc prefix(ftnType: FootnoteType): string = + case ftnType + of fnManualNumber: result = "footnote-" + of fnAutoNumber: result = "footnoteauto-" + of fnAutoNumberLabel: result = "footnote-" + of fnAutoSymbol: result = "footnotesym-" + of fnCitation: result = "citation-" + +proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} = + ## Parses footnotes and citations, always returns 2 sons: + ## + ## 1) footnote label, always containing rnInner with 1 or more sons + ## 2) footnote body, which may be nil + var label: PRstNode + if isRst(p): + inc p.idx # skip space after `..` + label = parseFootnoteName(p, reference=false) + if label == nil: + if isRst(p): + dec p.idx + return nil + result = newRstNode(rnFootnote) + result.add label + let (fnType, i) = getFootnoteType(p.s, label) + var name = "" + var anchor = fnType.prefix + case fnType + of fnManualNumber: + addFootnoteNumManual(p, i) + anchor.add $i + of fnAutoNumber, fnAutoNumberLabel: + name = rstnodeToRefname(label) + addFootnoteNumAuto(p, name) + if fnType == fnAutoNumberLabel: + anchor.add name + else: # fnAutoNumber + result.order = p.s.lineFootnoteNum.len + anchor.add $result.order + of fnAutoSymbol: + addFootnoteSymAuto(p) + result.order = p.s.lineFootnoteSym.len + anchor.add $p.s.lineFootnoteSym.len + of fnCitation: + anchor.add rstnodeToRefname(label) + addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor) + result.anchor = anchor + if currentTok(p).kind == tkWhite: inc p.idx + discard parseBlockContent(p, result, parseSectionWrapper) + if result.len < 2: + result.add nil + proc sonKind(father: PRstNode, i: int): RstNodeKind = result = rnLeaf if i < father.len: result = father.sons[i].kind @@ -3064,6 +3184,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = of rnLineBlock: a = parseLineBlock(p) of rnMarkdownBlockQuote: a = parseMarkdownBlockQuote(p) of rnDirective: a = parseDotDot(p) + of rnFootnote: a = parseFootnote(p) of rnEnumList: a = parseEnumList(p) of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)") of rnParagraph: discard @@ -3089,12 +3210,6 @@ proc parseSection(p: var RstParser, result: PRstNode) = result.sons[0] = newRstNode(rnInner, result.sons[0].sons, anchor=result.sons[0].anchor) -proc parseSectionWrapper(p: var RstParser): PRstNode = - result = newRstNode(rnInner) - parseSection(p, result) - while result.kind == rnInner and result.len == 1: - result = result.sons[0] - proc parseDoc(p: var RstParser): PRstNode = result = parseSectionWrapper(p) if currentTok(p).kind != tkEof: @@ -3104,7 +3219,6 @@ type DirFlag = enum hasArg, hasOptions, argIsFile, argIsWord DirFlags = set[DirFlag] - SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.} proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode = ## Parses arguments and options for a directive block. @@ -3147,21 +3261,6 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode popInd(p) result.add(options) -proc indFollows(p: RstParser): bool = - result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) - -proc parseBlockContent(p: var RstParser, father: var PRstNode, - contentParser: SectionParser): bool {.gcsafe.} = - ## parse the final content part of explicit markup blocks (directives, - ## footnotes, etc). Returns true if succeeded. - if currentTok(p).kind != tkIndent or indFollows(p): - let blockIndent = getWrappableIndent(p) - pushInd(p, blockIndent) - let content = contentParser(p) - popInd(p) - father.add content - result = true - proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags, contentParser: SectionParser): PRstNode = ## A helper proc that does main work for specific directive procs. @@ -3398,54 +3497,6 @@ proc selectDir(p: var RstParser, d: string): PRstNode = else: rstMessage(p, meInvalidDirective, d, tok.line, tok.col) -proc prefix(ftnType: FootnoteType): string = - case ftnType - of fnManualNumber: result = "footnote-" - of fnAutoNumber: result = "footnoteauto-" - of fnAutoNumberLabel: result = "footnote-" - of fnAutoSymbol: result = "footnotesym-" - of fnCitation: result = "citation-" - -proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} = - ## Parses footnotes and citations, always returns 2 sons: - ## - ## 1) footnote label, always containing rnInner with 1 or more sons - ## 2) footnote body, which may be nil - inc p.idx - let label = parseFootnoteName(p, reference=false) - if label == nil: - dec p.idx - return nil - result = newRstNode(rnFootnote) - result.add label - let (fnType, i) = getFootnoteType(label) - var name = "" - var anchor = fnType.prefix - case fnType - of fnManualNumber: - addFootnoteNumManual(p, i) - anchor.add $i - of fnAutoNumber, fnAutoNumberLabel: - name = rstnodeToRefname(label) - addFootnoteNumAuto(p, name) - if fnType == fnAutoNumberLabel: - anchor.add name - else: # fnAutoNumber - result.order = p.s.lineFootnoteNum.len - anchor.add $result.order - of fnAutoSymbol: - addFootnoteSymAuto(p) - result.order = p.s.lineFootnoteSym.len - anchor.add $p.s.lineFootnoteSym.len - of fnCitation: - anchor.add rstnodeToRefname(label) - addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor) - result.anchor = anchor - if currentTok(p).kind == tkWhite: inc p.idx - discard parseBlockContent(p, result, parseSectionWrapper) - if result.len < 2: - result.add nil - proc parseDotDot(p: var RstParser): PRstNode = # parse "explicit markup blocks" result = nil @@ -3729,7 +3780,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = of rnRstRef, rnPandocRef: result = resolveLink(s, n) of rnFootnote: - var (fnType, num) = getFootnoteType(n.sons[0]) + var (fnType, num) = getFootnoteType(s, n.sons[0]) case fnType of fnManualNumber, fnCitation: discard "no need to alter fixed text" @@ -3747,7 +3798,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = n.sons[0].sons[0].text = sym n.sons[1] = resolveSubs(s, n.sons[1]) of rnFootnoteRef: - var (fnType, num) = getFootnoteType(n.sons[0]) + var (fnType, num) = getFootnoteType(s, n.sons[0]) template addLabel(number: int | string) = var nn = newRstNode(rnInner) nn.add newLeaf($number) diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 329adc101eb3..e39eae9c1f63 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -531,6 +531,73 @@ suite "RST parsing": ```' """ + test "Markdown footnotes": + # Testing also 1) correct order of manually-numbered and automatically- + # numbered footnotes; 2) no spaces between references (html & 3 below): + + check(dedent""" + Paragraph [^1] [^html-hyphen][^3] and [^latex] + + [^1]: footnote1 + + [^html-hyphen]: footnote2 + continuation2 + + [^latex]: footnote4 + + [^3]: footnote3 + continuation3 + """.toAst == + dedent""" + rnInner + rnInner + rnLeaf 'Paragraph' + rnLeaf ' ' + rnFootnoteRef + rnInner + rnLeaf '1' + rnLeaf 'footnote-1' + rnLeaf ' ' + rnFootnoteRef + rnInner + rnLeaf '2' + rnLeaf 'footnote-htmlminushyphen' + rnFootnoteRef + rnInner + rnLeaf '3' + rnLeaf 'footnote-3' + rnLeaf ' ' + rnLeaf 'and' + rnLeaf ' ' + rnFootnoteRef + rnInner + rnLeaf '4' + rnLeaf 'footnote-latex' + rnFootnoteGroup + rnFootnote anchor='footnote-1' + rnInner + rnLeaf '1' + rnLeaf 'footnote1' + rnFootnote anchor='footnote-htmlminushyphen' + rnInner + rnLeaf '2' + rnInner + rnLeaf 'footnote2' + rnLeaf ' ' + rnLeaf 'continuation2' + rnFootnote anchor='footnote-latex' + rnInner + rnLeaf '4' + rnLeaf 'footnote4' + rnFootnote anchor='footnote-3' + rnInner + rnLeaf '3' + rnInner + rnLeaf 'footnote3' + rnLeaf ' ' + rnLeaf 'continuation3' + """) + test "Markdown code blocks with more > 3 backticks": check(dedent""" ```` diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 8c68f68c9810..934403665461 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1128,7 +1128,7 @@ Test1 Paragraph2 ref `internal anchor`_. """ - let output9 = input9.toHtml + let output9 = input9.toHtml(preferRst) # _`internal anchor` got erased: check "href=\"#internal-anchor\"" notin output9 check "href=\"#citation-another\"" in output9 @@ -1156,7 +1156,7 @@ Test1 doAssert "<a href=\"#citation-third\">[Third]</a>" in output10 let input11 = ".. [note]\n" # should not crash - let output11 = input11.toHtml + let output11 = input11.toHtml(preferRst) doAssert "<a href=\"#citation-note\">[note]</a>" in output11 # check that references to auto-numbered footnotes work @@ -1443,7 +1443,7 @@ Test1 Ref. target103_. """ - let output2 = input2.toHtml + let output2 = input2.toHtml(preferRst) # "target101" should be erased and changed to "section-xyz": doAssert "href=\"#target300\"" notin output2 doAssert "id=\"target300\"" notin output2 From d13aab50cf465a7f2edf9c37a4fa30e128892e72 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:36:45 +0800 Subject: [PATCH 244/347] fixes branches interacting with break, raise etc. in strictdefs (#22627) ```nim {.experimental: "strictdefs".} type Test = object id: int proc test(): Test = if true: return Test() else: return echo test() ``` I will tackle https://github.com/nim-lang/Nim/issues/16735 and #21615 in the following PR. The old code just premises that in branches ended with returns, raise statements etc. , all variables including the result variable are initialized for that branch. It's true for noreturn statements. But it is false for the result variable in a branch tailing with a return statement, in which the result variable is not initialized. The solution is not perfect for usages below branch statements with the result variable uninitialized, but it should suffice for now, which gives a proper warning. It also fixes ```nim {.experimental: "strictdefs".} type Test = object id: int proc foo {.noreturn.} = discard proc test9(x: bool): Test = if x: foo() else: foo() ``` which gives a warning, but shouldn't --- compiler/lookups.nim | 2 +- compiler/sempass2.nim | 87 +++++++++++++++++++++++++++++-------- tests/init/tcompiles.nim | 64 +++++++++++++++++++++++++++ tests/init/treturns.nim | 93 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 20 deletions(-) create mode 100644 tests/init/tcompiles.nim create mode 100644 tests/init/treturns.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 90f9a9b2b472..2bdf3a1e0725 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -596,7 +596,7 @@ proc lookUp*(c: PContext, n: PNode): PSym = if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident) else: internalError(c.config, n.info, "lookUp") - return + return nil if amb: #contains(c.ambiguousSymbols, result.id): result = errorUseQualifier(c, n.info, result, amb) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 0954f42fd01f..823699a8cdbd 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -352,20 +352,25 @@ proc useVar(a: PEffects, n: PNode) = a.init.add s.id useVarNoInitCheck(a, n, s) +type + BreakState = enum + bsNone + bsBreakOrReturn + bsNoReturn type TIntersection = seq[tuple[id, count: int]] # a simple count table -proc addToIntersection(inter: var TIntersection, s: int, initOnly: bool) = +proc addToIntersection(inter: var TIntersection, s: int, state: BreakState) = for j in 0..<inter.len: if s == inter[j].id: - if not initOnly: + if state == bsNone: inc inter[j].count return - if initOnly: - inter.add((id: s, count: 0)) - else: + if state == bsNone: inter.add((id: s, count: 1)) + else: + inter.add((id: s, count: 0)) proc throws(tracked, n, orig: PNode) = if n.typ == nil or n.typ.kind != tyError: @@ -469,7 +474,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = track(tracked, n[0]) dec tracked.inTryStmt for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i], false) + addToIntersection(inter, tracked.init[i], bsNone) var branches = 1 var hasFinally = false @@ -504,7 +509,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = tracked.init.add b[j][2].sym.id track(tracked, b[^1]) for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i], false) + addToIntersection(inter, tracked.init[i], bsNone) else: setLen(tracked.init, oldState) track(tracked, b[^1]) @@ -673,15 +678,50 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar localError(tracked.config, n.info, $n & " is not GC safe") notNilCheck(tracked, n, paramType) -proc breaksBlock(n: PNode): bool = + +proc breaksBlock(n: PNode): BreakState = # semantic check doesn't allow statements after raise, break, return or # call to noreturn proc, so it is safe to check just the last statements var it = n while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: it = it.lastSon - result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or - it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + case it.kind + of nkBreakStmt, nkReturnStmt: + result = bsBreakOrReturn + of nkRaiseStmt: + result = bsNoReturn + of nkCallKinds: + if it[0].kind == nkSym and sfNoReturn in it[0].sym.flags: + result = bsNoReturn + else: + result = bsNone + else: + result = bsNone + +proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter: var int, + hasBreaksBlock: BreakState, oldState: int, resSym: PSym, hasResult: bool) = + if hasResult: + var alreadySatisfy = false + + if hasBreaksBlock == bsNoReturn: + alreadySatisfy = true + inc resCounter + + for i in oldState..<tracked.init.len: + if tracked.init[i] == resSym.id: + if not alreadySatisfy: + inc resCounter + alreadySatisfy = true + else: + addToIntersection(inter, tracked.init[i], hasBreaksBlock) + else: + for i in oldState..<tracked.init.len: + addToIntersection(inter, tracked.init[i], hasBreaksBlock) + +template hasResultSym(s: PSym): bool = + s != nil and s.kind in {skProc, skFunc, skConverter, skMethod} and + not isEmptyType(s.typ[0]) proc trackCase(tracked: PEffects, n: PNode) = track(tracked, n[0]) @@ -694,6 +734,10 @@ proc trackCase(tracked: PEffects, n: PNode) = (tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features) var inter: TIntersection = @[] var toCover = 0 + let hasResult = hasResultSym(tracked.owner) + let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil + var resCounter = 0 + for i in 1..<n.len: let branch = n[i] setLen(tracked.init, oldState) @@ -703,13 +747,14 @@ proc trackCase(tracked: PEffects, n: PNode) = for i in 0..<branch.len: track(tracked, branch[i]) let hasBreaksBlock = breaksBlock(branch.lastSon) - if not hasBreaksBlock: + if hasBreaksBlock == bsNone: inc toCover - for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i], hasBreaksBlock) + addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult) setLen(tracked.init, oldState) if not stringCase or lastSon(n).kind == nkElse: + if hasResult and resCounter == n.len-1: + tracked.init.add resSym.id for id, count in items(inter): if count >= toCover: tracked.init.add id # else we can't merge @@ -723,14 +768,17 @@ proc trackIf(tracked: PEffects, n: PNode) = addFact(tracked.guards, n[0][0]) let oldState = tracked.init.len + let hasResult = hasResultSym(tracked.owner) + let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil + var resCounter = 0 + var inter: TIntersection = @[] var toCover = 0 track(tracked, n[0][1]) let hasBreaksBlock = breaksBlock(n[0][1]) - if not hasBreaksBlock: + if hasBreaksBlock == bsNone: inc toCover - for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i], hasBreaksBlock) + addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult) for i in 1..<n.len: let branch = n[i] @@ -743,13 +791,14 @@ proc trackIf(tracked: PEffects, n: PNode) = for i in 0..<branch.len: track(tracked, branch[i]) let hasBreaksBlock = breaksBlock(branch.lastSon) - if not hasBreaksBlock: + if hasBreaksBlock == bsNone: inc toCover - for i in oldState..<tracked.init.len: - addToIntersection(inter, tracked.init[i], hasBreaksBlock) + addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult) setLen(tracked.init, oldState) if lastSon(n).len == 1: + if hasResult and resCounter == n.len: + tracked.init.add resSym.id for id, count in items(inter): if count >= toCover: tracked.init.add id # else we can't merge as it is not exhaustive diff --git a/tests/init/tcompiles.nim b/tests/init/tcompiles.nim new file mode 100644 index 000000000000..2072702ad744 --- /dev/null +++ b/tests/init/tcompiles.nim @@ -0,0 +1,64 @@ +discard """ + matrix: "--warningAsError:ProveInit --warningAsError:Uninit" +""" + +{.experimental: "strictdefs".} + +type Test = object + id: int + +proc foo {.noreturn.} = discard + +block: + proc test(x: bool): Test = + if x: + foo() + else: + foo() + +block: + proc test(x: bool): Test = + if x: + result = Test() + else: + foo() + + discard test(true) + +block: + proc test(x: bool): Test = + if x: + result = Test() + else: + return Test() + + discard test(true) + +block: + proc test(x: bool): Test = + if x: + return Test() + else: + return Test() + + discard test(true) + +block: + proc test(x: bool): Test = + if x: + result = Test() + else: + result = Test() + return + + discard test(true) + +block: + proc test(x: bool): Test = + if x: + result = Test() + return + else: + raise newException(ValueError, "unreachable") + + discard test(true) diff --git a/tests/init/treturns.nim b/tests/init/treturns.nim new file mode 100644 index 000000000000..77469472a84c --- /dev/null +++ b/tests/init/treturns.nim @@ -0,0 +1,93 @@ +{.experimental: "strictdefs".} + +type Test = object + id: int + +proc foo {.noreturn.} = discard + +proc test1(): Test = + if true: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return Test() + else: + return + +proc test0(): Test = + if true: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return + else: + foo() + +proc test2(): Test = + if true: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return + else: + return + +proc test3(): Test = + if true: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return + else: + return Test() + +proc test4(): Test = + if true: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return + else: + result = Test() + return + +proc test5(x: bool): Test = + case x: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + of true: + return + else: + return Test() + +proc test6(x: bool): Test = + case x: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + of true: + return + else: + return + +proc test7(x: bool): Test = + case x: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + of true: + return + else: + discard + +proc test8(x: bool): Test = + case x: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + of true: + discard + else: + raise + +proc hasImportStmt(): bool = + if false: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return true + else: + discard + +discard hasImportStmt() + +block: + proc hasImportStmt(): bool = + if false: #[tt.Warning + ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]# + return true + else: + return + + discard hasImportStmt() From 3fbb078a3c0f37dc328a1d95e1e2215118481f7b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 5 Sep 2023 05:09:27 +0800 Subject: [PATCH 245/347] update checkout to v4 (#22640) ref https://github.com/actions/checkout/issues/1448 probably nodejs needs to be updated to 20.x --- .github/workflows/bisects.yml | 2 +- .github/workflows/ci_bench.yml | 4 ++-- .github/workflows/ci_docs.yml | 2 +- .github/workflows/ci_packages.yml | 2 +- .github/workflows/ci_publish.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml index a8200c1f9654..362c7315805c 100644 --- a/.github/workflows/bisects.yml +++ b/.github/workflows/bisects.yml @@ -8,7 +8,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # nimrun-action requires Nim installed. - uses: jiro4989/setup-nim-action@v1 diff --git a/.github/workflows/ci_bench.yml b/.github/workflows/ci_bench.yml index 68f0007228fe..535b52355b44 100644 --- a/.github/workflows/ci_bench.yml +++ b/.github/workflows/ci_bench.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 60 # refs bug #18178 steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 @@ -60,7 +60,7 @@ jobs: run: nim c -r -d:release ci/action.nim - name: 'Checkout minimize' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: 'nim-lang/ci_bench' path: minimize diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index cde30e5fa8a5..d605b63016c5 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -55,7 +55,7 @@ jobs: steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 0bec4cc212b8..1dabd6077140 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -28,7 +28,7 @@ jobs: NIM_TESTAMENT_BATCH: ${{ matrix.batch }} steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml index d6b628d8cef4..26c617a8e3e8 100644 --- a/.github/workflows/ci_publish.yml +++ b/.github/workflows/ci_publish.yml @@ -17,7 +17,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 From 8f7aedb3d1941650e6650c07aa4bcf6a7dee0b90 Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Mon, 4 Sep 2023 22:18:58 +0100 Subject: [PATCH 246/347] Add `hasDefaultValue` type trait (#22636) Needed for #21842. --- changelog.md | 3 ++- compiler/semmagic.nim | 2 ++ lib/pure/typetraits.nim | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index fb0102cd3af1..4ea436fb0a67 100644 --- a/changelog.md +++ b/changelog.md @@ -11,7 +11,8 @@ [//]: # "Additions:" -- Adds `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. +- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. +- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value. [//]: # "Deprecations:" diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index d06b32e47a89..2b57d1873ae5 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -197,6 +197,8 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) let complexObj = containsGarbageCollectedRef(t) or hasDestructor(t) result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph) + of "hasDefaultValue": + result = newIntNodeT(toInt128(ord(not operand.requiresInit)), traitCall, c.idgen, c.graph) of "isNamedTuple": var operand = operand.skipTypes({tyGenericInst}) let cond = operand.kind == tyTuple and operand.n != nil diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 70eb1b81c0c4..384c2dbac94a 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -96,6 +96,23 @@ proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".} ## ## Other languages name a type like these `blob`:idx:. +proc hasDefaultValue*(t: typedesc): bool {.magic: "TypeTrait".} = + ## Returns true if `t` has a valid default value. + runnableExamples: + {.experimental: "strictNotNil".} + type + NilableObject = ref object + a: int + Object = NilableObject not nil + RequiresInit[T] = object + a {.requiresInit.}: T + + assert hasDefaultValue(NilableObject) + assert not hasDefaultValue(Object) + assert hasDefaultValue(string) + assert not hasDefaultValue(var string) + assert not hasDefaultValue(RequiresInit[int]) + proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} = ## Returns true for named tuples, false for any other type. runnableExamples: From 6000cc8c0f5c71fd0495172b5680ab6b1945428c Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Tue, 5 Sep 2023 11:30:13 +0300 Subject: [PATCH 247/347] fix sym of created generic instantiation type (#22642) fixes #22639 A `tyGenericInst` has its last son as the instantiated body of the original generic type. However this type keeps its original `sym` field from the original generic types, which means the sym's type is uninstantiated. This causes problems in the implementation of `getType`, where it uses the `sym` fields of types to represent them in AST, the relevant example for the issue being [here](https://github.com/nim-lang/Nim/blob/d13aab50cf465a7f2edf9c37a4fa30e128892e72/compiler/vmdeps.nim#L191) called from [here](https://github.com/nim-lang/Nim/blob/d13aab50cf465a7f2edf9c37a4fa30e128892e72/compiler/vmdeps.nim#L143). To fix this, create a new symbol from the original symbol for the instantiated body during the creation of `tyGenericInst`s with the appropriate type. Normally `replaceTypeVarsS` would be used for this, but here it seems to cause some recursion issue (immediately gives an error like "cannot instantiate HSlice[T, U]"), so we directly set the symbol's type to the instantiated type. Avoiding recursion means we also cannot use `replaceTypeVarsN` for the symbol AST, and the symbol not having any AST crashes the implementation of `getType` again [here](https://github.com/nim-lang/Nim/blob/d13aab50cf465a7f2edf9c37a4fa30e128892e72/compiler/vmdeps.nim#L167), so the symbol AST is set to the original generic type AST for now which is what it was before anyway. Not sure about this because not sure why the recursion issue is happening, putting it at the end of the proc doesn't help either. Also not sure if the `cl.owner != nil and s.owner != cl.owner` condition from `replaceTypeVarsS` is relevant here. This might also break some code if it depended on the original generic type symbol being given. --- compiler/semtypinst.nim | 12 +++++++++++ compiler/sigmatch.nim | 12 +++++++++-- compiler/suggest.nim | 6 ++---- tests/generics/timpl_ast.nim | 39 ++++++++++++++++++++++++++++++++---- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d01ca28e4322..54ea9769c6e2 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -433,6 +433,18 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # One step is enough, because the recursive nature of # handleGenericInvocation will handle the alias-to-alias-to-alias case if newbody.isGenericAlias: newbody = newbody.skipGenericAlias + + let origSym = newbody.sym + if origSym != nil and sfFromGeneric notin origSym.flags: + # same as `replaceTypeVarsS` but directly set the type without recursion: + newbody.sym = copySym(origSym, cl.c.idgen) + incl(newbody.sym.flags, sfFromGeneric) + newbody.sym.owner = origSym.owner + newbody.sym.typ = newbody + # unfortunately calling `replaceTypeVarsN` causes recursion, so this AST + # is the original generic body AST + newbody.sym.ast = copyTree(origSym.ast) + rawAddSon(result, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody) if not cl.allowMetaTypes: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index bb99463b64dd..a6cda65b15a2 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -505,6 +505,13 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = if r.kind == tyObject and ptrs <= 1: result = r else: result = nil +proc getObjectSym(t: PType): PSym = + if tfFromGeneric in t.flags and t.typeInst.kind == tyGenericInst: + var dummy: SkippedPtr + result = t.typeInst[0].skipToObject(dummy).sym + else: + result = t.sym + proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool = assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} var askip = skippedNone @@ -512,11 +519,12 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin var t = a.skipToObject(askip) let r = f.skipToObject(fskip) if r == nil: return false + let rSym = getObjectSym(r) var depth = 0 var last = a # XXX sameObjectType can return false here. Need to investigate # why that is but sameObjectType does way too much work here anyway. - while t != nil and r.sym != t.sym and askip == fskip: + while t != nil and rSym != getObjectSym(t) and askip == fskip: t = t[0] if t == nil: break last = t @@ -1602,7 +1610,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # crossing path with metatypes/aliases, so we need to separate them # by checking sym.id let genericSubtype = isGenericSubtype(c, x, f, depth, f) - if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody: + if not (genericSubtype and getObjectSym(aobj).id != getObjectSym(fobj).id) and aOrig.kind != tyGenericBody: depth = -1 if depth >= 0: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index c7efad1af64e..5554991bd736 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -280,10 +280,8 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = for module in c.friendModules: if fmoduleId == module.id: return true if f.kind == skField: - var symObj = f.owner - if symObj.typ.skipTypes({tyGenericBody, tyGenericInst, tyGenericInvocation, tyAlias}).kind in {tyRef, tyPtr}: - symObj = symObj.typ.toObjectFromRefPtrGeneric.sym - assert symObj != nil + var symObj = f.owner.typ.toObjectFromRefPtrGeneric.sym + assert symObj != nil for scope in allScopes(c.currentScope): for sym in scope.allowPrivateAccess: if symObj.id == sym.id: return true diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim index 69a59e24d4ab..97fe128cd20d 100644 --- a/tests/generics/timpl_ast.nim +++ b/tests/generics/timpl_ast.nim @@ -1,7 +1,3 @@ -discard """ - action: compile -""" - import std/macros import std/assertions @@ -47,3 +43,38 @@ block: # issues #9899, ##14708 macro parse(s: static string) = result = parseStmt(s) parse("type " & implRepr(Option)) + +block: # issue #22639 + type + Spectrum[N: static int] = object + data: array[N, float] + AngleInterpolator = object + data: seq[Spectrum[60]] + proc initInterpolator(num: int): AngleInterpolator = + result = AngleInterpolator() + for i in 0 ..< num: + result.data.add Spectrum[60]() + macro genCompatibleTuple(t: typed): untyped = + let typ = t.getType[1].getTypeImpl[2] + result = nnkTupleTy.newTree() + for i, ch in typ: # is `nnkObjectTy` + result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs` + ch[1], + newEmptyNode()) + proc fullSize[T: object | tuple](x: T): int = + var tmp: genCompatibleTuple(T) + result = 0 + for field, val in fieldPairs(x): + result += sizeof(val) + doAssert result == sizeof(tmp) + + let reflectivity = initInterpolator(1) + for el in reflectivity.data: + doAssert fullSize(el) == sizeof(el) + doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0]) + doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]] + doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]] + type Foo[T] = object + data: T + doAssert genCompatibleTuple(Foo[int]) is tuple[data: int] + doAssert genCompatibleTuple(Foo[float]) is tuple[data: float] From eb91cf991aa9840639cc358c21d503e4cf27e268 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:31:28 +0800 Subject: [PATCH 248/347] fixes #22619; don't lift cursor fields in the hook calls (#22638) fixes https://github.com/nim-lang/Nim/issues/22619 It causes double free for closure iterators because cursor fields are destroyed in the lifted destructors of `Env`. Besides, according to the Nim manual > In fact, cursor more generally prevents object construction/destruction pairs and so can also be useful in other contexts. At least, destruction of cursor fields might cause troubles. todo - [x] tests - [x] revert a certain old PR --------- Co-authored-by: zerbina <100542850+zerbina@users.noreply.github.com> --- compiler/liftdestructors.nim | 3 +- tests/iter/t22619.nim | 79 ++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 tests/iter/t22619.nim diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index f28586addb4a..387a22555ef7 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -162,8 +162,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) if c.filterDiscriminator != nil: return let f = n.sym let b = if c.kind == attachedTrace: y else: y.dotField(f) - if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and - c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or + if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or enforceDefaultOp: defaultOp(c, f.typ, body, x.dotField(f), b) else: diff --git a/tests/iter/t22619.nim b/tests/iter/t22619.nim new file mode 100644 index 000000000000..04e707633c28 --- /dev/null +++ b/tests/iter/t22619.nim @@ -0,0 +1,79 @@ +# bug #22619 + +when false: # todo fixme + block: + type + Resource = object + value: int + + Object = object + r {.cursor.}: Resource + s {.cursor.}: seq[Resource] + + var numDestroy = 0 + + proc `=copy`(x: var Resource, y: Resource) {.error.} # disallow full copies + proc `=destroy`(x: Resource) = + inc numDestroy + + proc test() = + # perform the test in procedure so that globals aren't used (their different + # semantics with regards to destruction would interfere) + var + r = Resource(value: 1) # initialize a resource + s = @[Resource(value: 2)] + + # make sure no copy is required in the initializer expression: + var o = Object(r: r, s: s) + + # copying the object doesn't perform a full copy of the cursor fields: + var o2 = o + discard addr(o2) # prevent `o2` from being turned into a cursor + + # check that the fields were shallow-copied: + doAssert o2.r.value == 1 + doAssert o2.s[0].value == 2 + + # make sure no copy is required with normal field assignments: + o.r = r + o.s = s + + + # when `o` and `o2` are destroyed, their destructor must not be called on + # their fields + + test() + + # one call for the `r` local and one for the object in `s` + doAssert numDestroy == 2 + +block: + type Value = distinct int + + var numDestroy = 0 + + proc `=destroy`(x: Value) = + inc numDestroy + + iterator iter(s: seq[Value]): int {.closure.} = + # because it is used across yields, `s2` is lifted into the iterator's + # environment. Since non-ref cursors in object didn't have their hooks + # disabled inside the environments lifted hooks, this led to double + # frees + var s2 {.cursor.} = s + var i = 0 + let L = s2.len + while i < L: + yield s2[i].int + inc i + + proc test() = + var s = @[Value(1), Value(2)] + let cl = iter + # make sure resuming the iterator works: + doAssert cl(s) == 1 + doAssert cl(s) == 2 + doAssert cl(s) == 0 + + test() + doAssert numDestroy == 2 From 90f87bcab769f3555454bbd210129670767cffbf Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Wed, 6 Sep 2023 05:45:07 +0300 Subject: [PATCH 249/347] fully revert generic inst sym change, test #22646 (#22653) reverts #22642, reopens #22639, closes #22646, refs #22650, refs https://github.com/alaviss/union/issues/51, refs #22652 The fallout is too much from #22642, we can come back to it if we can account for all the affected code. --- compiler/semtypinst.nim | 11 ----------- compiler/sigmatch.nim | 12 ++---------- tests/distinct/tborrow.nim | 32 ++++++++++++++++++++++++++++++++ tests/generics/timpl_ast.nim | 35 ----------------------------------- 4 files changed, 34 insertions(+), 56 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 54ea9769c6e2..0157ecd0b65a 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -434,17 +434,6 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # handleGenericInvocation will handle the alias-to-alias-to-alias case if newbody.isGenericAlias: newbody = newbody.skipGenericAlias - let origSym = newbody.sym - if origSym != nil and sfFromGeneric notin origSym.flags: - # same as `replaceTypeVarsS` but directly set the type without recursion: - newbody.sym = copySym(origSym, cl.c.idgen) - incl(newbody.sym.flags, sfFromGeneric) - newbody.sym.owner = origSym.owner - newbody.sym.typ = newbody - # unfortunately calling `replaceTypeVarsN` causes recursion, so this AST - # is the original generic body AST - newbody.sym.ast = copyTree(origSym.ast) - rawAddSon(result, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody) if not cl.allowMetaTypes: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a6cda65b15a2..bb99463b64dd 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -505,13 +505,6 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = if r.kind == tyObject and ptrs <= 1: result = r else: result = nil -proc getObjectSym(t: PType): PSym = - if tfFromGeneric in t.flags and t.typeInst.kind == tyGenericInst: - var dummy: SkippedPtr - result = t.typeInst[0].skipToObject(dummy).sym - else: - result = t.sym - proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool = assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} var askip = skippedNone @@ -519,12 +512,11 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin var t = a.skipToObject(askip) let r = f.skipToObject(fskip) if r == nil: return false - let rSym = getObjectSym(r) var depth = 0 var last = a # XXX sameObjectType can return false here. Need to investigate # why that is but sameObjectType does way too much work here anyway. - while t != nil and rSym != getObjectSym(t) and askip == fskip: + while t != nil and r.sym != t.sym and askip == fskip: t = t[0] if t == nil: break last = t @@ -1610,7 +1602,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # crossing path with metatypes/aliases, so we need to separate them # by checking sym.id let genericSubtype = isGenericSubtype(c, x, f, depth, f) - if not (genericSubtype and getObjectSym(aobj).id != getObjectSym(fobj).id) and aOrig.kind != tyGenericBody: + if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody: depth = -1 if depth >= 0: diff --git a/tests/distinct/tborrow.nim b/tests/distinct/tborrow.nim index 8a40982a8dc7..e34248de5b77 100644 --- a/tests/distinct/tborrow.nim +++ b/tests/distinct/tborrow.nim @@ -98,3 +98,35 @@ block: # issue #22069 MuscleCar = Car[128] var x: MuscleCar doAssert x.color is array[128, int] + +block: # issue #22646 + type + Vec[N : static[int], T: SomeNumber] = object + arr: array[N, T] + Vec3[T: SomeNumber] = Vec[3, T] + + proc `[]=`[N,T](v: var Vec[N,T]; ix:int; c:T): void {.inline.} = v.arr[ix] = c + proc `[]`[N,T](v: Vec[N,T]; ix: int): T {.inline.} = v.arr[ix] + + proc dot[N,T](u,v: Vec[N,T]): T {. inline .} = discard + proc length[N,T](v: Vec[N,T]): T = discard + proc cross[T](v1,v2:Vec[3,T]): Vec[3,T] = discard + proc normalizeWorks[T](v: Vec[3,T]): Vec[3,T] = discard ## <- Explicit size makes it work! + proc foo[N,T](u, v: Vec[N,T]): Vec[N,T] = discard ## <- broken + proc normalize[N,T](v: Vec[N,T]): Vec[N,T] = discard ## <- broken + + type Color = distinct Vec3[float] + + template borrowOps(typ: typedesc): untyped = + proc `[]=`(v: var typ; ix: int; c: float): void {.borrow.} + proc `[]`(v: typ; ix: int): float {.borrow.} + proc dot(v, u: typ): float {.borrow.} + proc cross(v, u: typ): typ {.borrow.} + proc length(v: typ): float {.borrow.} + proc normalizeWorks(v: typ): typ {.borrow.} ## Up to here everything works + proc foo(u, v: typ): typ {.borrow.} ## Broken + proc normalize(v: typ): typ {.borrow.} ## Broken + borrowOps(Color) + var x: Vec[3, float] + let y = Color(x) + doAssert Vec3[float](y) == x diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim index 97fe128cd20d..016128e303a1 100644 --- a/tests/generics/timpl_ast.nim +++ b/tests/generics/timpl_ast.nim @@ -43,38 +43,3 @@ block: # issues #9899, ##14708 macro parse(s: static string) = result = parseStmt(s) parse("type " & implRepr(Option)) - -block: # issue #22639 - type - Spectrum[N: static int] = object - data: array[N, float] - AngleInterpolator = object - data: seq[Spectrum[60]] - proc initInterpolator(num: int): AngleInterpolator = - result = AngleInterpolator() - for i in 0 ..< num: - result.data.add Spectrum[60]() - macro genCompatibleTuple(t: typed): untyped = - let typ = t.getType[1].getTypeImpl[2] - result = nnkTupleTy.newTree() - for i, ch in typ: # is `nnkObjectTy` - result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs` - ch[1], - newEmptyNode()) - proc fullSize[T: object | tuple](x: T): int = - var tmp: genCompatibleTuple(T) - result = 0 - for field, val in fieldPairs(x): - result += sizeof(val) - doAssert result == sizeof(tmp) - - let reflectivity = initInterpolator(1) - for el in reflectivity.data: - doAssert fullSize(el) == sizeof(el) - doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0]) - doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]] - doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]] - type Foo[T] = object - data: T - doAssert genCompatibleTuple(Foo[int]) is tuple[data: int] - doAssert genCompatibleTuple(Foo[float]) is tuple[data: float] From 009ce1e17ea80f026764ecabf888703869e6bb82 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:05:01 +0800 Subject: [PATCH 250/347] add union to packages (#22658) --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index a21ea41b705d..59f3be0259f6 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -168,6 +168,7 @@ pkg "timezones" pkg "tiny_sqlite" pkg "unicodedb", "nim c -d:release -r tests/tests.nim" pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" +pkg "union", "nim c -r tests/treadme.nim", url = "https://github.com/alaviss/union" pkg "unpack" pkg "weave", "nimble test_gc_arc", useHead = true pkg "websocket", "nim c websocket.nim" From b9f039e0c39c8f4e10ebdbe77e9a91f2595e1cbe Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Wed, 6 Sep 2023 12:37:51 +0300 Subject: [PATCH 251/347] switch back to main neo in CI (#22660) refs https://github.com/andreaferretti/neo/pull/53 --- testament/important_packages.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 59f3be0259f6..873da0dbd08a 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -96,8 +96,7 @@ pkg "measuremancer", "nimble testDeps; nimble -y test" pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" -pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim", "https://github.com/metagn/neo" -# remove custom url when https://github.com/andreaferretti/neo/pull/53 is merged +pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim" pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true pkg "netty" pkg "nico", allowFailure = true From ed9e3cba07c9d03714b4ea22e50dcc7e706e0bed Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Thu, 7 Sep 2023 06:30:37 +0300 Subject: [PATCH 252/347] make getType nodes of generic insts have full inst type (#22655) fixes #22639 for the third time Nodes generated by `getType` for `tyGenericInst` types, instead of having the original `tyGenericInst` type, will have the type of the last child (due to the `mapTypeToAst` calls which set the type to the given argument). This will cause subsequent `getType` calls to lose information and think it's OK to use the sym of the instantiated type rather than fully expand the generic instantiation. To prevent this, update the type of the node from the `mapTypeToAst` calls to the full generic instantiation type. --- compiler/vmdeps.nim | 4 ++++ tests/generics/timpl_ast.nim | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 863896419eb0..a47d034c7a46 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -133,6 +133,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; if inst: if allowRecursion: result = mapTypeToAstR(t.lastSon, info) + # keep original type info for getType calls on the output node: + result.typ = t else: result = newNodeX(nkBracketExpr) #result.add mapTypeToAst(t.lastSon, info) @@ -141,6 +143,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; result.add mapTypeToAst(t[i], info) else: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) + # keep original type info for getType calls on the output node: + result.typ = t of tyGenericBody: if inst: result = mapTypeToAstR(t.lastSon, info) diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim index 016128e303a1..97fe128cd20d 100644 --- a/tests/generics/timpl_ast.nim +++ b/tests/generics/timpl_ast.nim @@ -43,3 +43,38 @@ block: # issues #9899, ##14708 macro parse(s: static string) = result = parseStmt(s) parse("type " & implRepr(Option)) + +block: # issue #22639 + type + Spectrum[N: static int] = object + data: array[N, float] + AngleInterpolator = object + data: seq[Spectrum[60]] + proc initInterpolator(num: int): AngleInterpolator = + result = AngleInterpolator() + for i in 0 ..< num: + result.data.add Spectrum[60]() + macro genCompatibleTuple(t: typed): untyped = + let typ = t.getType[1].getTypeImpl[2] + result = nnkTupleTy.newTree() + for i, ch in typ: # is `nnkObjectTy` + result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs` + ch[1], + newEmptyNode()) + proc fullSize[T: object | tuple](x: T): int = + var tmp: genCompatibleTuple(T) + result = 0 + for field, val in fieldPairs(x): + result += sizeof(val) + doAssert result == sizeof(tmp) + + let reflectivity = initInterpolator(1) + for el in reflectivity.data: + doAssert fullSize(el) == sizeof(el) + doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0]) + doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]] + doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]] + type Foo[T] = object + data: T + doAssert genCompatibleTuple(Foo[int]) is tuple[data: int] + doAssert genCompatibleTuple(Foo[float]) is tuple[data: float] From ad7c1c38ff7d8f6bd328bd76a37c8ef844253e5d Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Thu, 7 Sep 2023 06:31:15 +0300 Subject: [PATCH 253/347] run docs CI on compiler changes (#22656) refs #22650 Docs CI cover standard library runnable examples that aren't covered by the test suite and can be affected by compiler changes without knowing --- .github/workflows/ci_docs.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index d605b63016c5..32e8e050e4a0 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -2,8 +2,7 @@ name: Nim Docs CI on: push: paths: - - 'compiler/docgen.nim' - - 'compiler/renderverbatim.nim' + - 'compiler/**.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' - 'doc/**.md' @@ -18,8 +17,7 @@ on: pull_request: # Run only on changes on these files. paths: - - 'compiler/docgen.nim' - - 'compiler/renderverbatim.nim' + - 'compiler/**.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' - 'doc/**.md' From e5106d1ef3b7fd6f9e67cdd5060af8868c4e7732 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Thu, 7 Sep 2023 06:33:01 +0300 Subject: [PATCH 254/347] minor refactoring, move some sym/type construction to semdata (#22654) Move `symFromType` and `symNodeFromType` from `sem`, and `isSelf` and `makeTypeDesc` from `concepts` into `semdata`. `makeTypeDesc` was moved out from semdata [when the `concepts` module was added](https://github.com/nim-lang/Nim/commit/6278b5d89af38e90aa30cfc4c217a2f4b1323339), so its old position might have been intended. If not, `isSelf` can also go in `ast`. --- compiler/concepts.nim | 14 -------------- compiler/sem.nim | 10 ---------- compiler/semdata.nim | 22 ++++++++++++++++++++++ compiler/semtypinst.nim | 2 -- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 681e4eace90f..55088740c888 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -13,8 +13,6 @@ import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets -from magicsys import addSonSkipIntLit - when defined(nimPreviewSlimSystem): import std/assertions @@ -33,18 +31,6 @@ proc declareSelf(c: PContext; info: TLineInfo) = s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow) addDecl(c, s, info) -proc isSelf*(t: PType): bool {.inline.} = - ## Is this the magical 'Self' type? - t.kind == tyTypeDesc and tfPacked in t.flags - -proc makeTypeDesc*(c: PContext, typ: PType): PType = - if typ.kind == tyTypeDesc and not isSelf(typ): - result = typ - else: - result = newTypeS(tyTypeDesc, c) - incl result.flags, tfCheckedForDestructor - result.addSonSkipIntLit(typ, c.idgen) - proc semConceptDecl(c: PContext; n: PNode): PNode = ## Recursive helper for semantic checking for the concept declaration. ## Currently we only support (possibly empty) lists of statements diff --git a/compiler/sem.nim b/compiler/sem.nim index 1a080f869f13..74045e5cf2b0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -329,16 +329,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode -proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym = - if t.sym != nil: return t.sym - result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info) - result.flags.incl sfAnon - result.typ = t - -proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = - result = newSymNode(symFromType(c, t, info), info) - result.typ = makeTypeDesc(c, t) - when false: proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = result = newEvalContext(c.module, mode) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 00559a5b6142..9386c3763f13 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -551,6 +551,28 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt; result.n = n addSonSkipIntLit(result, intType, c.idgen) # basetype of range +proc isSelf*(t: PType): bool {.inline.} = + ## Is this the magical 'Self' type from concepts? + t.kind == tyTypeDesc and tfPacked in t.flags + +proc makeTypeDesc*(c: PContext, typ: PType): PType = + if typ.kind == tyTypeDesc and not isSelf(typ): + result = typ + else: + result = newTypeS(tyTypeDesc, c) + incl result.flags, tfCheckedForDestructor + result.addSonSkipIntLit(typ, c.idgen) + +proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym = + if t.sym != nil: return t.sym + result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info) + result.flags.incl sfAnon + result.typ = t + +proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode = + result = newSymNode(symFromType(c, t, info), info) + result.typ = makeTypeDesc(c, t) + proc markIndirect*(c: PContext, s: PSym) {.inline.} = if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}: incl(s.flags, sfAddrTaken) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0157ecd0b65a..bd2b25c24e41 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -12,8 +12,6 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, lineinfos, modulegraphs -from concepts import makeTypeDesc - when defined(nimPreviewSlimSystem): import std/assertions From a4df44d9fbe3328b2dd3b7209b98f80fdd5d361a Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Thu, 7 Sep 2023 04:45:54 +0100 Subject: [PATCH 255/347] Remove some unnecessary initialization in string operations (#22579) * `prepareAdd` * `toNimStr` * `setLengthStrV2` * `NimAsgnStrV2` * `prepareMutation` * Some cleanups --- lib/system/strs_v2.nim | 98 ++++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 1c15d14711d6..e161172b24af 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -34,53 +34,72 @@ template frees(s) = else: dealloc(s.p) +template allocPayload(newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](allocShared(contentSize(newLen))) + else: + cast[ptr NimStrPayload](alloc(contentSize(newLen))) + +template allocPayload0(newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](allocShared0(contentSize(newLen))) + else: + cast[ptr NimStrPayload](alloc0(contentSize(newLen))) + +template reallocPayload(p: pointer, newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](reallocShared(p, contentSize(newLen))) + else: + cast[ptr NimStrPayload](realloc(p, contentSize(newLen))) + +template reallocPayload0(p: pointer; oldLen, newLen: int): ptr NimStrPayload = + when compileOption("threads"): + cast[ptr NimStrPayload](reallocShared0(p, contentSize(oldLen), contentSize(newLen))) + else: + cast[ptr NimStrPayload](realloc0(p, contentSize(oldLen), contentSize(newLen))) + proc resize(old: int): int {.inline.} = if old <= 0: result = 4 elif old <= high(int16): result = old * 2 else: result = old * 3 div 2 # for large arrays * 3/2 is better -proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = - let newLen = s.len + addlen +proc prepareAdd(s: var NimStringV2; addLen: int) {.compilerRtl.} = + let newLen = s.len + addLen if isLiteral(s): let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - when compileOption("threads"): - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(newLen))) - else: - s.p = cast[ptr NimStrPayload](alloc0(contentSize(newLen))) + s.p = allocPayload(newLen) s.p.cap = newLen if s.len > 0: # we are about to append, so there is no need to copy the \0 terminator: copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen)) + elif oldP == nil: + # In the case of `newString(0) & ""`, since `src.len == 0`, `appendString` + # will not set the `\0` terminator, so we set it here. + s.p.data[0] = '\0' else: let oldCap = s.p.cap and not strlitFlag if newLen > oldCap: let newCap = max(newLen, resize(oldCap)) - when compileOption("threads"): - s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) - else: - s.p = cast[ptr NimStrPayload](realloc0(s.p, contentSize(oldCap), contentSize(newCap))) + s.p = reallocPayload(s.p, newCap) s.p.cap = newCap + if newLen < newCap: + zeroMem(cast[pointer](addr s.p.data[newLen+1]), newCap - newLen) proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inl.} = #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag): prepareAdd(s, 1) s.p.data[s.len] = c - s.p.data[s.len+1] = '\0' inc s.len + s.p.data[s.len] = '\0' proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) - else: - var p = cast[ptr NimStrPayload](alloc0(contentSize(len))) + var p = allocPayload(len) p.cap = len - if len > 0: - # we are about to append, so there is no need to copy the \0 terminator: - copyMem(unsafeAddr p.data[0], str, len) + copyMem(unsafeAddr p.data[0], str, len+1) result = NimStringV2(len: len, p: p) proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} = @@ -99,18 +118,15 @@ proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inlin proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} = dest.p.data[dest.len] = c - dest.p.data[dest.len+1] = '\0' inc dest.len + dest.p.data[dest.len] = '\0' proc rawNewString(space: int): NimStringV2 {.compilerproc.} = # this is also 'system.newStringOfCap'. if space <= 0: result = NimStringV2(len: 0, p: nil) else: - when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared(contentSize(space))) - else: - var p = cast[ptr NimStrPayload](alloc(contentSize(space))) + var p = allocPayload(space) p.cap = space p.data[0] = '\0' result = NimStringV2(len: 0, p: p) @@ -119,10 +135,7 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - when compileOption("threads"): - var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) - else: - var p = cast[ptr NimStrPayload](alloc0(contentSize(len))) + var p = allocPayload0(len) p.cap = len result = NimStringV2(len: len, p: p) @@ -130,8 +143,25 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} = if newLen == 0: discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations" else: - if newLen > s.len or isLiteral(s): - prepareAdd(s, newLen - s.len) + if isLiteral(s): + let oldP = s.p + s.p = allocPayload(newLen) + s.p.cap = newLen + if s.len > 0: + copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen)) + if newLen > s.len: + zeroMem(cast[pointer](addr s.p.data[s.len]), newLen - s.len + 1) + else: + s.p.data[newLen] = '\0' + else: + zeroMem(cast[pointer](addr s.p.data[0]), newLen + 1) + elif newLen > s.len: + let oldCap = s.p.cap and not strlitFlag + if newLen > oldCap: + let newCap = max(newLen, resize(oldCap)) + s.p = reallocPayload0(s.p, oldCap, newCap) + s.p.cap = newCap + s.p.data[newLen] = '\0' s.len = newLen @@ -148,10 +178,7 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = # 'let y = newStringOfCap(); var x = y' # on the other hand... These get turned into moves now. frees(a) - when compileOption("threads"): - a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len))) - else: - a.p = cast[ptr NimStrPayload](alloc0(contentSize(b.len))) + a.p = allocPayload(b.len) a.p.cap = b.len a.len = b.len copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) @@ -159,10 +186,7 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = proc nimPrepareStrMutationImpl(s: var NimStringV2) = let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - when compileOption("threads"): - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) - else: - s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len))) + s.p = allocPayload(s.len) s.p.cap = s.len copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1) From ee4a219012ca1fcb2cfb2cce4e87ff6774009d86 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Thu, 7 Sep 2023 05:46:45 +0200 Subject: [PATCH 256/347] Fix #17509: Continue instead of return with unfinished generics (#22563) Close #17509 Current knowledge: - delaying cache fixes the issue - changing return of `if inst.len < key.len:` in `searchInstTypes` to `continue` fixes the issue. With return the broken types are also cached over and over Related issues are completely unaffected as of now, so there must be something deeper. I am also still trying to find the true cause, so feel free to ignore for now --------- Co-authored-by: SirOlaf <> --- compiler/semtypinst.nim | 3 ++- tests/generics/t17509.nim | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/generics/t17509.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index bd2b25c24e41..4ab0e2de10da 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -45,7 +45,8 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = # XXX: This happens for prematurely cached # types such as Channel[empty]. Why? # See the notes for PActor in handleGenericInvocation - return + # if this is return the same type gets cached more than it needs to + continue if not sameFlags(inst, key): continue diff --git a/tests/generics/t17509.nim b/tests/generics/t17509.nim new file mode 100644 index 000000000000..89f507577dcb --- /dev/null +++ b/tests/generics/t17509.nim @@ -0,0 +1,25 @@ +type List[O] = object + next: ptr List[O] + +proc initList[O](l: ptr List[O]) = + l[].next = l + +type + PolytopeVertex[R] = object + list: List[PolytopeVertex[R]] + + PolytopeEdge[R] = object + list: List[PolytopeEdge[R]] + + Polytope[R] = object + vertices: List[PolytopeVertex[R]] + edges: List[PolytopeEdge[R]] + +var pt: Polytope[float] + +static: + doAssert pt.vertices.next is (ptr List[PolytopeVertex[float]]) + doAssert pt.edges.next is (ptr List[PolytopeEdge[float]]) + +initList(addr pt.vertices) +initList(addr pt.edges) \ No newline at end of file From 2a8c759df0e8a952f7cbea8539e3fb7aa234795a Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Fri, 8 Sep 2023 06:50:39 +0200 Subject: [PATCH 257/347] Fix #21742: Check generic alias depth before skip (#22443) Close #21742 Checking if there's any side-effects and if just changing typeRel is adequate for this issue before trying to look into related ones. `skipBoth` is also not that great, it can lead to code that works sometimes but fails when the proc is instantiated with branching aliases. This is mostly an issue with error clarity though. --------- Co-authored-by: SirOlaf <unknown> Co-authored-by: SirOlaf <> --- compiler/sigmatch.nim | 8 ++++++-- compiler/types.nim | 7 +++++++ tests/generics/t21742.nim | 10 ++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/generics/t21742.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index bb99463b64dd..1cf29dcfd465 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1456,8 +1456,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let origF = f var f = if prev == nil: f else: prev - let roota = a.skipGenericAlias - let rootf = f.skipGenericAlias + let deptha = a.genericAliasDepth() + let depthf = f.genericAliasDepth() + let skipBoth = deptha == depthf and (a.len > 0 and f.len > 0 and a.base != f.base) + + let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a + let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f if a.kind == tyGenericInst: if roota.base == rootf.base: diff --git a/compiler/types.nim b/compiler/types.nim index 4dc9d36f5f13..f5e965df98ff 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1175,6 +1175,13 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool = proc isGenericAlias*(t: PType): bool = return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst +proc genericAliasDepth*(t: PType): int = + result = 0 + var it = t + while it.isGenericAlias: + it = it.lastSon + inc result + proc skipGenericAlias*(t: PType): PType = return if t.isGenericAlias: t.lastSon else: t diff --git a/tests/generics/t21742.nim b/tests/generics/t21742.nim new file mode 100644 index 000000000000..c49c8ee97c7c --- /dev/null +++ b/tests/generics/t21742.nim @@ -0,0 +1,10 @@ +type + Foo[T] = object + x:T + Bar[T,R] = Foo[T] + Baz = Bar[int,float] + +proc qux[T,R](x: Bar[T,R]) = discard + +var b:Baz +b.qux() \ No newline at end of file From d45270bdf721e195a9dae344f9a3285d066c3932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Fri, 8 Sep 2023 09:46:40 +0100 Subject: [PATCH 258/347] fixes #22662 Procs with constructor pragma doesn't initialize object's fields (#22665) fixes #22662 Procs with constructor pragma doesn't initialize object's fields --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/ccgtypes.nim | 9 ++++++++- tests/cpp/tconstructor.nim | 23 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index b7363f67fe87..87dcb02ad59e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -649,6 +649,13 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope = result = rope(mangleField(m, field.name)) if result == "": internalError(m.config, field.info, "mangleRecFieldName") +proc hasCppCtor(m: BModule; typ: PType): bool = + result = false + if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType: + for prc in m.g.graph.memberProcsPerType[typ.itemId]: + if sfConstructor in prc.flags: + return true + proc genRecordFieldsAux(m: BModule; n: PNode, rectype: PType, check: var IntSet; result: var Rope; unionPrefix = "") = @@ -713,7 +720,7 @@ proc genRecordFieldsAux(m: BModule; n: PNode, else: # don't use fieldType here because we need the # tyGenericInst for C++ template support - if fieldType.isOrHasImportedCppType(): + if fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ): result.addf("\t$1$3 $2{};$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) else: result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim index d4d6a7ccfaa4..b69162661496 100644 --- a/tests/cpp/tconstructor.nim +++ b/tests/cpp/tconstructor.nim @@ -3,6 +3,10 @@ discard """ cmd: "nim cpp $file" output: ''' 1 +0 +123 +0 +123 ''' """ @@ -52,4 +56,21 @@ proc makeCppClass(): NimClass {. constructor: "NimClass() : CppClass(0, 0) ".} = this.x = 1 let nimClass = makeNimClass(1) -var nimClassDef {.used.}: NimClass #since we explictly defined the default constructor we can declare the obj \ No newline at end of file +var nimClassDef {.used.}: NimClass #since we explictly defined the default constructor we can declare the obj + +#bug: 22662 +type + BugClass* = object + x: int # Not initialized + +proc makeBugClass(): BugClass {.constructor.} = + discard + +proc main = + for i in 0 .. 1: + var n = makeBugClass() + echo n.x + n.x = 123 + echo n.x + +main() \ No newline at end of file From 5f13e15e0a6f90c462a71cd30addc677f688c4dc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 8 Sep 2023 23:05:57 +0800 Subject: [PATCH 259/347] fixes #22664; guard against potential seqs self assignments (#22671) fixes #22664 --- compiler/liftdestructors.nim | 11 +++++++++++ lib/system/seqs_v2.nim | 6 ++++++ tests/arc/tarcmisc.nim | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 387a22555ef7..a73df046ef8d 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -553,6 +553,14 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: body.sons.setLen counterIdx +proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) = + var cond = callCodegenProc(c.g, "sameSeqPayload", c.info, + newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), + newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y) + ) + cond.typ = getSysType(c.g, c.info, tyBool) + body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info))) + proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind of attachedDup: @@ -560,10 +568,13 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = forallElements(c, t, body, x, y) of attachedAsgn, attachedDeepCopy: # we generate: + # if x.p == y.p: + # return # setLen(dest, y.len) # var i = 0 # while i < y.len: dest[i] = y[i]; inc(i) # This is usually more efficient than a destroy/create pair. + checkSelfAssignment(c, t, body, x, y) body.add setLenSeqCall(c, t, x, y) forallElements(c, t, body, x, y) of attachedSink: diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 3a49142bf4bb..b71b8176252a 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -32,6 +32,10 @@ type len: int p: ptr NimSeqPayload[T] + NimRawSeq = object + len: int + p: pointer + const nimSeqVersion {.core.} = 2 # XXX make code memory safe for overflows in '*' @@ -139,6 +143,8 @@ proc newSeq[T](s: var seq[T], len: Natural) = shrink(s, 0) setLen(s, len) +proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerproc, inline.} = + result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p template capacityImpl(sek: NimSeqV2): int = if sek.p != nil: (sek.p.cap and not strlitFlag) else: 0 diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 1404f54a1bda..525c8c3a64d7 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -614,3 +614,24 @@ method process*(self: App): Option[Event] {.base.} = type Test2 = ref object of RootObj method bug(t: Test2): seq[float] {.base.} = discard + +block: # bug #22664 + type + ElementKind = enum String, Number + Element = object + case kind: ElementKind + of String: + str: string + of Number: + num: float + Calc = ref object + stack: seq[Element] + + var calc = new Calc + + calc.stack.add Element(kind: Number, num: 200.0) + doAssert $calc.stack == "@[(kind: Number, num: 200.0)]" + let calc2 = calc + calc2.stack = calc.stack # This nulls out the object in the stack + doAssert $calc.stack == "@[(kind: Number, num: 200.0)]" + doAssert $calc2.stack == "@[(kind: Number, num: 200.0)]" From e6ca13ec857dc065ebf3297129cc1538d4698f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Sat, 9 Sep 2023 09:34:20 +0100 Subject: [PATCH 260/347] Instantiates generics in the module that uses it (#22513) Attempts to move the generic instantiation to the module that uses it. This should decrease re-compilation times as the source module where the generic lives doesnt need to be recompiled --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/ast.nim | 3 ++- compiler/commands.nim | 5 ++++- compiler/ic/ic.nim | 2 ++ compiler/ic/packed_ast.nim | 1 + compiler/options.nim | 3 +++ compiler/pragmas.nim | 1 + compiler/sem.nim | 3 ++- compiler/semexprs.nim | 2 +- compiler/seminst.nim | 23 +++++++++++++++++++++-- compiler/semmagic.nim | 2 ++ compiler/vm.nim | 2 +- compiler/vmgen.nim | 2 +- tests/ic/tgenericinst.nim | 11 +++++++++++ 13 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 tests/ic/tgenericinst.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index e82a9a293417..8658251e52a6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -936,6 +936,7 @@ type # it won't cause problems # for skModule the string literal to output for # deprecated modules. + instantiatedFrom*: PSym # for instances, the generic symbol where it came from. when defined(nimsuggest): allUsages*: seq[TLineInfo] @@ -1936,7 +1937,7 @@ proc skipGenericOwner*(s: PSym): PSym = ## Generic instantiations are owned by their originating generic ## symbol. This proc skips such owners and goes straight to the owner ## of the generic itself (the module or the enclosing proc). - result = if s.kind in skProcKinds and sfFromGeneric in s.flags: + result = if s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule: s.owner.owner else: s.owner diff --git a/compiler/commands.nim b/compiler/commands.nim index ba996f77ede7..ac5366f10fc9 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -783,7 +783,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if conf.backend == backendJs or conf.cmd == cmdNimscript: discard else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info) #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe) - of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + of "tlsemulation": + processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + if optTlsEmulation in conf.globalOptions: + conf.legacyFeatures.incl emitGenerics of "implicitstatic": processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) of "patterns", "trmacros": diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 845e3d1fa6f9..8b0a3105438a 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -413,6 +413,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId p.annex = toPackedLib(s.annex, c, m) when hasFFI: p.cname = toLitId(s.cname, m) + p.instantiatedFrom = s.instantiatedFrom.safeItemId(c, m) # fill the reserved slot, nothing else: m.syms[s.itemId.item] = p @@ -876,6 +877,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; if externalName != "": result.loc.r = rope externalName result.loc.flags = s.locFlags + result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom) proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym = if s == nilItemId: diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 8eafa5e968ae..b87348c5a049 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -71,6 +71,7 @@ type when hasFFI: cname*: LitId constraint*: NodeId + instantiatedFrom*: PackedItemId PackedType* = object kind*: TTypeKind diff --git a/compiler/options.nim b/compiler/options.nim index bda86a598953..c18ca92dc189 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -235,6 +235,9 @@ type laxEffects ## Lax effects system prior to Nim 2.0. verboseTypeMismatch + emitGenerics + ## generics are emitted in the module that contains them. + ## Useful for libraries that rely on local passC SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 49b3b819e4f0..01df9bbcd9d1 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1129,6 +1129,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wLocalPassc: assert sym != nil and sym.kind == skModule let s = expectStrLit(c, it) + appendToModule(sym, n) extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex)) recordPragma(c, it, "localpassl", s) of wPush: diff --git a/compiler/sem.nim b/compiler/sem.nim index 74045e5cf2b0..55c6a427f519 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -19,7 +19,8 @@ import intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, strtabs, int128, - isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs + isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs, + extccomp when not defined(leanCompiler): diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 001ce958a80c..d62e2561050b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2543,7 +2543,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags: # may have been resolved to `@`[empty] at some point, # reset to `@` to deal with this - n[0] = newSymNode(n[0].sym.owner, n[0].info) + n[0] = newSymNode(n[0].sym.instantiatedFrom, n[0].info) n[1] = semExpr(c, n[1], flags, arrayType) result = semDirectOp(c, n, flags, expectedType) else: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 61480494b283..1dba1ebdc346 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -313,6 +313,17 @@ proc fillMixinScope(c: PContext) = addSym(c.currentScope, n.sym) p = p.next +proc getLocalPassC(c: PContext, s: PSym): string = + if s.ast == nil or s.ast.len == 0: return "" + result = "" + template extractPassc(p: PNode) = + if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc": + return p[0][1].strVal + extractPassc(s.ast[0]) #it is set via appendToModule in pragmas (fast access) + for n in s.ast: + for p in n: + extractPassc(p) + proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. @@ -328,14 +339,22 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var n = copyTree(fn.ast) # NOTE: for access of private fields within generics from a different module # we set the friend module: - c.friendModules.add(getModule(fn)) + let producer = getModule(fn) + c.friendModules.add(producer) let oldMatchedConcept = c.matchedConcept c.matchedConcept = nil let oldScope = c.currentScope while not isTopLevel(c): c.currentScope = c.currentScope.parent result = copySym(fn, c.idgen) incl(result.flags, sfFromGeneric) - result.owner = fn + result.instantiatedFrom = fn + if sfGlobal in result.flags and c.config.symbolFiles != disabledSf: + let passc = getLocalPassC(c, producer) + if passc != "": #pass the local compiler options to the consumer module too + extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex)) + result.owner = c.module + else: + result.owner = fn result.ast = n pushOwner(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 2b57d1873ae5..cb5e76e8c672 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -502,6 +502,8 @@ proc semNewFinalize(c: PContext; n: PNode): PNode = getAttachedOp(c.graph, t, attachedDestructor).owner == fin: discard "already turned this one into a finalizer" else: + if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move + fin.owner = fin.instantiatedFrom let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info) let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen)) selfSymNode.typ = fin.typ[1] diff --git a/compiler/vm.nim b/compiler/vm.nim index 18b26486587b..579bab600c78 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1313,7 +1313,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if a.kind == nkSym and a.sym.kind in skProcKinds and b.kind == nkSym and b.sym.kind in skProcKinds: regs[ra].intVal = - if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1 + if sfFromGeneric in a.sym.flags and a.sym.instantiatedFrom == b.sym: 1 else: 0 else: stackTrace(c, tos, pc, "node is not a proc symbol") diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 33e1d8463f7d..16911e6408cf 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -2098,7 +2098,7 @@ proc toKey(s: PSym): string = result.add s.name.s if s.owner != nil: if sfFromGeneric in s.flags: - s = s.owner.owner + s = s.instantiatedFrom.owner else: s = s.owner result.add "." diff --git a/tests/ic/tgenericinst.nim b/tests/ic/tgenericinst.nim new file mode 100644 index 000000000000..3346764f544f --- /dev/null +++ b/tests/ic/tgenericinst.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim cpp --incremental:on $file" +""" + +{.emit:"""/*TYPESECTION*/ +#include <iostream> + struct Foo { }; +""".} + +type Foo {.importcpp.} = object +echo $Foo() #Notice the generic is instantiate in the this module if not, it wouldnt find Foo \ No newline at end of file From 5717a4843d648c3ec99e2416b65809ea829c1ab9 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 9 Sep 2023 23:25:48 +0800 Subject: [PATCH 261/347] fixes #22676; remove wMerge which is a noop for more than 8 years (#22678) fixes #22676 If csource or CI forbids it, we can always fall back to adding it to the nonPragmaWords list. I doubt it was used outside of the system since it was used to implement & or something for magics. --- compiler/pragmas.nim | 5 +---- compiler/wordrecg.nim | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 01df9bbcd9d1..5fb74406bea7 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -29,7 +29,7 @@ const ## common pragmas for declarations, to a good approximation procPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, - wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge, + wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wBorrow, wImportCompilerProc, wThread, wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, @@ -971,9 +971,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) incl(sym.flags, sfGlobal) incl(sym.flags, sfPure) - of wMerge: - # only supported for backwards compat, doesn't do anything anymore - noVal(c, it) of wConstructor: incl(sym.flags, sfConstructor) if sfImportc notin sym.flags: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 1724b18f6ad7..aa25f7fd1aa7 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -49,7 +49,7 @@ type wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align", wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header", wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn", - wNosinks = "nosinks", wMerge = "merge", wLib = "lib", wDynlib = "dynlib", + wNosinks = "nosinks", wLib = "lib", wDynlib = "dynlib", wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", wHint = "hint", From 8853fb07753756dbe29364e8a32081462367215d Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Sat, 9 Sep 2023 20:11:45 +0100 Subject: [PATCH 262/347] Make `newSeqOfCap` not initialize memory. (#21842) It's used in `newSeqUninitialized`. --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- compiler/ccgexprs.nim | 2 +- lib/system.nim | 2 +- lib/system/mm/go.nim | 2 +- lib/system/seqs_v2.nim | 11 ++++++++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8bd0b872158a..d1c0ac7b1858 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1463,7 +1463,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = var a: TLoc = initLocExpr(p, e[1]) if optSeqDestructors in p.config.globalOptions: if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) - linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", + linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n", [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype), ]) diff --git a/lib/system.nim b/lib/system.nim index c3cad4f7119a..4949430a3469 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -925,7 +925,7 @@ proc reset*[T](obj: var T) {.noSideEffect.} = obj = default(typeof(obj)) proc setLen*[T](s: var seq[T], newlen: Natural) {. - magic: "SetLengthSeq", noSideEffect.} + magic: "SetLengthSeq", noSideEffect, nodestroy.} ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type. ## ## If the current length is greater than the new length, diff --git a/lib/system/mm/go.nim b/lib/system/mm/go.nim index 9ec25fb65634..8f3aeb964c69 100644 --- a/lib/system/mm/go.nim +++ b/lib/system/mm/go.nim @@ -109,7 +109,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = writebarrierptr(addr(result), newSeq(typ, len)) proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} = - result = newObj(typ, align(GenericSeqSize, typ.base.align) + cap * typ.base.size) + result = newObjNoInit(typ, align(GenericSeqSize, typ.base.align) + cap * typ.base.size) cast[PGenericSeq](result).len = 0 cast[PGenericSeq](result).reserved = cap cast[PGenericSeq](result).elemSize = typ.base.size diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index b71b8176252a..81790bfe7fe9 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -50,6 +50,15 @@ proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises else: result = nil +proc newSeqPayloadUninit(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} = + # Used in `newSeqOfCap()`. + if cap > 0: + var p = cast[ptr NimSeqPayloadBase](alignedAlloc(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign)) + p.cap = cap + result = p + else: + result = nil + template `+!`(p: pointer, s: int): pointer = cast[pointer](cast[int](p) +% s) @@ -125,7 +134,7 @@ proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, n # We also save the `wasMoved + destroy` pair for the sink parameter. xu.p.data[oldLen] = y -proc setLen[T](s: var seq[T], newlen: Natural) = +proc setLen[T](s: var seq[T], newlen: Natural) {.nodestroy.} = {.noSideEffect.}: if newlen < s.len: shrink(s, newlen) From 2ce9197d3ad705b3abb5f40f0c27543d7bf03381 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:43:46 +0800 Subject: [PATCH 263/347] [minor] merge similar branches in vmgen (#22683) --- compiler/vmgen.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 16911e6408cf..05c0d2a67777 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -2232,8 +2232,6 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkVarSection, nkLetSection: unused(c, n, dest) genVarSection(c, n) - of declarativeDefs, nkMacroDef: - unused(c, n, dest) of nkLambdaKinds: #let s = n[namePos].sym #discard genProc(c, s) @@ -2253,7 +2251,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, - nkMixinStmt, nkBindStmt: + nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef: unused(c, n, dest) of nkStringToCString, nkCStringToString: gen(c, n[0], dest) From cd24195d442301b5012020fbd627686f10833c87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Sun, 10 Sep 2023 11:30:03 +0100 Subject: [PATCH 264/347] fixes #22679 Nim zero clear an object contains C++ imported class when a proc return it (#22681) --- compiler/cgen.nim | 7 ++++--- tests/cpp/t22679.nim | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 tests/cpp/t22679.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 6d301dc475c7..678a15bc9fa9 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -498,9 +498,10 @@ proc resetLoc(p: BProc, loc: var TLoc) = else: # array passed as argument decayed into pointer, bug #7332 # so we use getTypeDesc here rather than rdLoc(loc) - linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", - [addrLoc(p.config, loc), - getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))]) + if not isOrHasImportedCppType(typ): #bug 22679 + linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", + [addrLoc(p.config, loc), + getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))]) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) diff --git a/tests/cpp/t22679.nim b/tests/cpp/t22679.nim new file mode 100644 index 000000000000..81defcb58e8d --- /dev/null +++ b/tests/cpp/t22679.nim @@ -0,0 +1,50 @@ +discard """ + cmd: "nim cpp $file" + output:''' +cppNZ.x = 123 +cppNZInit.x = 123 +hascpp.cppnz.x = 123 +hasCppInit.cppnz.x = 123 +hasCppCtor.cppnz.x = 123 +''' +""" +{.emit:"""/*TYPESECTION*/ +struct CppNonZero { + int x = 123; +}; +""".} + +import sugar +type + CppNonZero {.importcpp, inheritable.} = object + x: cint + + HasCpp = object + cppnz: CppNonZero + +proc initCppNonZero: CppNonZero = + CppNonZero() + +proc initHasCpp: HasCpp = + HasCpp() + +proc ctorHasCpp: HasCpp {.constructor.} = + discard + +proc main = + var cppNZ: CppNonZero + dump cppNZ.x + + var cppNZInit = initCppNonZero() + dump cppNZInit.x + + var hascpp: HasCpp + dump hascpp.cppnz.x + + var hasCppInit = initHasCpp() + dump hasCppInit.cppnz.x + + var hasCppCtor = ctorHasCpp() + dump hasCppCtor.cppnz.x + +main() \ No newline at end of file From 8032f252b2a338b01129f6331b60da937e6df8bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Sun, 10 Sep 2023 11:45:36 +0100 Subject: [PATCH 265/347] fixes #22669 constructor pragma doesnt init Nim default fields (#22670) fixes #22669 constructor pragma doesnt init Nim default fields --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/ccgexprs.nim | 44 ++++++++++++++++++++++---------------- compiler/cgen.nim | 5 +++++ compiler/semstmts.nim | 2 ++ tests/cpp/tconstructor.nim | 35 +++++++++++++++++++++++++++++- 4 files changed, 66 insertions(+), 20 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index d1c0ac7b1858..4521664bfd81 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1494,8 +1494,27 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = else: result = false + +proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) = + var tmp2: TLoc = default(TLoc) + tmp2.r = r + let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r) + if field.loc.r == "": fillObjectFields(p.module, ty) + if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr") + if check != nil and optFieldCheck in p.options: + genFieldCheck(p, check, r, field) + tmp2.r.add(".") + tmp2.r.add(field.loc.r) + if useTemp: + tmp2.k = locTemp + tmp2.storage = if isRef: OnHeap else: OnStack + else: + tmp2.k = d.k + tmp2.storage = if isRef: OnHeap else: d.storage + tmp2.lode = val + expr(p, val, tmp2) + proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = - #echo renderTree e, " ", e.isDeepConstExpr # inheritance in C++ does not allow struct initialization so # we skip this step here: if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions: @@ -1534,24 +1553,11 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = discard getTypeDesc(p.module, t) let ty = getUniqueType(t) for i in 1..<e.len: - let it = e[i] - var tmp2: TLoc = default(TLoc) - tmp2.r = r - let field = lookupFieldAgain(p, ty, it[0].sym, tmp2.r) - if field.loc.r == "": fillObjectFields(p.module, ty) - if field.loc.r == "": internalError(p.config, e.info, "genObjConstr") - if it.len == 3 and optFieldCheck in p.options: - genFieldCheck(p, it[2], r, field) - tmp2.r.add(".") - tmp2.r.add(field.loc.r) - if useTemp: - tmp2.k = locTemp - tmp2.storage = if isRef: OnHeap else: OnStack - else: - tmp2.k = d.k - tmp2.storage = if isRef: OnHeap else: d.storage - tmp2.lode = it[1] - expr(p, it[1], tmp2) + var check: PNode = nil + if e[i].len == 3 and optFieldCheck in p.options: + check = e[i][2] + genFieldObjConstr(p, ty, useTemp, isRef, e[i][0], e[i][1], check, d, r, e.info) + if useTemp: if d.k == locNone: d = tmp diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 678a15bc9fa9..f8d62e1af147 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1171,6 +1171,11 @@ proc genProcAux*(m: BModule, prc: PSym) = returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) elif sfConstructor in prc.flags: fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) + let ty = resNode.sym.typ[0] #generate nim's ctor + for i in 1..<resNode.sym.ast.len: + let field = resNode.sym.ast[i] + genFieldObjConstr(p, ty, useTemp = false, isRef = false, + field[0], field[1], check = nil, resNode.sym.loc, "(*this)", tmpInfo) else: fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ[0]) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 08d6d44e6e8c..5a72632efc22 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1711,6 +1711,8 @@ proc addThis(c: PContext, n: PNode, t: PType, owner: TSymKind) = c.p.resultSym = s n.add newSymNode(c.p.resultSym) addParamOrResult(c, c.p.resultSym, owner) + #resolves nim's obj ctor inside cpp ctors see #22669 + s.ast = c.semExpr(c, newTree(nkCall, t[0].sym.ast[0])) proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = template genResSym(s) = diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim index b69162661496..32fb5e1a27b6 100644 --- a/tests/cpp/tconstructor.nim +++ b/tests/cpp/tconstructor.nim @@ -7,6 +7,15 @@ discard """ 123 0 123 +___ +0 +777 +10 +123 +0 +777 +10 +123 ''' """ @@ -73,4 +82,28 @@ proc main = n.x = 123 echo n.x -main() \ No newline at end of file +main() +#bug: +echo "___" +type + NimClassWithDefault = object + x: int + y = 777 + case kind: bool = true + of true: + z: int = 10 + else: discard + +proc makeNimClassWithDefault(): NimClassWithDefault {.constructor.} = + discard + +proc init = + for i in 0 .. 1: + var n = makeNimClassWithDefault() + echo n.x + echo n.y + echo n.z + n.x = 123 + echo n.x + +init() \ No newline at end of file From f8f6a3c9269da2862f74e53858cf9c0072ebbdc7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:35:40 +0800 Subject: [PATCH 266/347] renderIr should print the actual return assign node (#22682) follow up https://github.com/nim-lang/Nim/pull/10806 Eventually we need a new option to print high level IR. It's confusing when I'm debugging the compiler without showing `return result = 1` using the expandArc option. For ```nim proc foo: int = return 2 ``` It now outputs when expanding ARC IR ```nim proc foo: int = return result = 2 ``` --- compiler/renderer.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c73fa994510d..41078716b11c 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -586,7 +586,7 @@ proc lsub(g: TSrcGen; n: PNode): int = if n.len > 1: result = MaxLineLen + 1 else: result = lsons(g, n) + len("using_") of nkReturnStmt: - if n.len > 0 and n[0].kind == nkAsgn: + if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags: result = len("return_") + lsub(g, n[0][1]) else: result = len("return_") + lsub(g, n[0]) @@ -1635,7 +1635,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n[0]) of nkReturnStmt: putWithSpace(g, tkReturn, "return") - if n.len > 0 and n[0].kind == nkAsgn: + if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags: gsub(g, n[0], 1) else: gsub(g, n, 0) From fbb5ac512cd03a67a3364469132a881341e10e1c Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Sun, 10 Sep 2023 16:36:49 +0100 Subject: [PATCH 267/347] Remove some unnecessary initialization in `seq` operations (#22677) * `PrepareSeqAdd` * `add` * `setLen` * `grow` Merge after #21842. --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- lib/system/seqs_v2.nim | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 81790bfe7fe9..c3e48019c701 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -79,15 +79,42 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin var p = cast[ptr NimSeqPayloadBase](p) let oldCap = p.cap and not strlitFlag let newCap = max(resize(oldCap), len+addlen) + var q: ptr NimSeqPayloadBase if (p.cap and strlitFlag) == strlitFlag: - var q = cast[ptr NimSeqPayloadBase](alignedAlloc0(headerSize + elemSize * newCap, elemAlign)) + q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign)) + copyMem(q +! headerSize, p +! headerSize, len * elemSize) + else: + let oldSize = headerSize + elemSize * oldCap + let newSize = headerSize + elemSize * newCap + q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign)) + + zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize) + q.cap = newCap + result = q + +proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. + noSideEffect, tags: [], raises: [], compilerRtl.} = + {.noSideEffect.}: + let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign) + if addlen <= 0: + result = p + elif p == nil: + result = newSeqPayloadUninit(len+addlen, elemSize, elemAlign) + else: + # Note: this means we cannot support things that have internal pointers as + # they get reallocated here. This needs to be documented clearly. + var p = cast[ptr NimSeqPayloadBase](p) + let oldCap = p.cap and not strlitFlag + let newCap = max(resize(oldCap), len+addlen) + if (p.cap and strlitFlag) == strlitFlag: + var q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign)) copyMem(q +! headerSize, p +! headerSize, len * elemSize) q.cap = newCap result = q else: let oldSize = headerSize + elemSize * oldCap let newSize = headerSize + elemSize * newCap - var q = cast[ptr NimSeqPayloadBase](alignedRealloc0(p, oldSize, newSize, elemAlign)) + var q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign)) q.cap = newCap result = q @@ -104,16 +131,17 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} = {.noSideEffect.}: cast[ptr NimSeqV2[T]](addr x).len = newLen -proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = +proc grow*[T](x: var seq[T]; newLen: Natural; value: T) {.nodestroy.} = let oldLen = x.len #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'" if newLen <= oldLen: return var xu = cast[ptr NimSeqV2[T]](addr x) if xu.p == nil or (xu.p.cap and not strlitFlag) < newLen: - xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T))) + xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T))) xu.len = newLen for i in oldLen .. newLen-1: - xu.p.data[i] = value + wasMoved(xu.p.data[i]) + `=copy`(xu.p.data[i], value) proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} = ## Generic proc for adding a data item `y` to a container `x`. @@ -126,7 +154,7 @@ proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, n let oldLen = x.len var xu = cast[ptr NimSeqV2[T]](addr x) if xu.p == nil or (xu.p.cap and not strlitFlag) < oldLen+1: - xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T), alignof(T))) + xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, 1, sizeof(T), alignof(T))) xu.len = oldLen+1 # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a # copyMem(). This is fine as know by construction that @@ -143,7 +171,7 @@ proc setLen[T](s: var seq[T], newlen: Natural) {.nodestroy.} = if newlen <= oldLen: return var xu = cast[ptr NimSeqV2[T]](addr s) if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen: - xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T))) + xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T))) xu.len = newlen for i in oldLen..<newlen: xu.p.data[i] = default(T) From b1a8d6976ff41a1cde84647b7dbade9316fbcecf Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:54:41 +0800 Subject: [PATCH 268/347] fixes the `discVal` register is used after free in vmgen (#22688) follow up https://github.com/nim-lang/Nim/pull/11955 --- compiler/vmgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 05c0d2a67777..2e54812a0564 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1822,7 +1822,6 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags let setLit = c.genx(checkExpr[1]) var rs = c.getTemp(getSysType(c.graph, n.info, tyBool)) c.gABC(n, opcContainsSet, rs, setLit, discVal) - c.freeTemp(discVal) c.freeTemp(setLit) # If the check fails let the user know let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs) @@ -1835,6 +1834,7 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags strLit.typ = strType c.genLit(strLit, msgReg) c.gABC(n, opcInvalidField, msgReg, discVal) + c.freeTemp(discVal) c.freeTemp(msgReg) c.patch(lab1) From 7e86cd6fa713914380d9ce2a71a2523f2eb83280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Mon, 11 Sep 2023 11:55:11 +0100 Subject: [PATCH 269/347] fixes #22680 Nim zero clear an object inherits C++ imported class when a proc return it (#22684) --- compiler/cgen.nim | 13 +++++++++--- tests/cpp/t22680.nim | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 tests/cpp/t22680.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f8d62e1af147..ad501aa94894 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -498,10 +498,17 @@ proc resetLoc(p: BProc, loc: var TLoc) = else: # array passed as argument decayed into pointer, bug #7332 # so we use getTypeDesc here rather than rdLoc(loc) - if not isOrHasImportedCppType(typ): #bug 22679 + let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc)) + if p.module.compileToCpp and isOrHasImportedCppType(typ): + if lfIndirect in loc.flags: + #C++ cant be just zeroed. We need to call the ctors + var tmp = getTemp(p, loc.t) + linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", + [addrLoc(p.config, loc), addrLoc(p.config, tmp), tyDesc]) + else: linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", - [addrLoc(p.config, loc), - getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))]) + [addrLoc(p.config, loc), tyDesc]) + # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) diff --git a/tests/cpp/t22680.nim b/tests/cpp/t22680.nim new file mode 100644 index 000000000000..80f1a8319b96 --- /dev/null +++ b/tests/cpp/t22680.nim @@ -0,0 +1,50 @@ +discard """ + cmd: "nim cpp $file" + output:''' +cppNZ.x = 123 +cppNZInit.x = 123 +inheritCpp.x = 123 +inheritCppInit.x = 123 +inheritCppCtor.x = 123 +''' +""" +import std/sugar + +{.emit:"""/*TYPESECTION*/ +struct CppNonZero { + int x = 123; +}; +""".} + +type + CppNonZero {.importcpp, inheritable.} = object + x: cint + + InheritCpp = object of CppNonZero + +proc initCppNonZero: CppNonZero = + CppNonZero() + +proc initInheritCpp: InheritCpp = + InheritCpp() + +proc ctorInheritCpp: InheritCpp {.constructor.} = + discard + +proc main = + var cppNZ: CppNonZero + dump cppNZ.x + + var cppNZInit = initCppNonZero() + dump cppNZInit.x + + var inheritCpp: InheritCpp + dump inheritCpp.x + + var inheritCppInit = initInheritCpp() + dump inheritCppInit.x + + var inheritCppCtor = ctorInheritCpp() + dump inheritCppCtor.x + +main() \ No newline at end of file From 8f5b90f886501862bd27d4e0e8244e2f7f0ebc2a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf <rumpf_a@web.de> Date: Mon, 11 Sep 2023 18:48:20 +0200 Subject: [PATCH 270/347] produce better code for object constructions and 'result' [backport] (#22668) --- compiler/ccgexprs.nim | 8 ++++++-- compiler/cgen.nim | 22 ++++++++++++++++------ tests/misc/trunner.nim | 11 +++++++---- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 4521664bfd81..b2c5a5ca8fac 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1537,6 +1537,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = var tmp: TLoc = default(TLoc) var r: Rope + let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags if useTemp: tmp = getTemp(p, t) r = rdLoc(tmp) @@ -1545,10 +1546,13 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = t = t.lastSon.skipTypes(abstractInstOwned) r = "(*$1)" % [r] gcUsage(p.config, e) - else: + elif needsZeroMem: constructLoc(p, tmp) + else: + genObjectInit(p, cpsStmts, t, tmp, constructObj) else: - resetLoc(p, d) + if needsZeroMem: resetLoc(p, d) + else: genObjectInit(p, cpsStmts, d.t, d, if isRef: constructRefObj else: constructObj) r = rdLoc(d) discard getTypeDesc(p.module, t) let ty = getUniqueType(t) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ad501aa94894..2197947bfdfe 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -984,11 +984,19 @@ proc closureSetup(p: BProc, prc: PSym) = linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", [rdLoc(env.loc), getTypeDesc(p.module, env.typ)]) +const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, + nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} + + declarativeDefs + proc containsResult(n: PNode): bool = result = false case n.kind - of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams: + of succ(nkEmpty)..pred(nkSym), succ(nkSym)..nkNilLit, harmless: discard + of nkReturnStmt: + for i in 0..<n.len: + if containsResult(n[i]): return true + result = n.len > 0 and n[0].kind == nkEmpty of nkSym: if n.sym.kind == skResult: result = true @@ -996,10 +1004,6 @@ proc containsResult(n: PNode): bool = for i in 0..<n.len: if containsResult(n[i]): return true -const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, - nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} + - declarativeDefs - proc easyResultAsgn(n: PNode): PNode = result = nil case n.kind @@ -1174,7 +1178,13 @@ proc genProcAux*(m: BModule, prc: PSym) = # declare the result symbol: assignLocalVar(p, resNode) assert(res.loc.r != "") - initLocalVar(p, res, immediateAsgn=false) + if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and + allPathsAsgnResult(procBody) == InitSkippable: + # In an ideal world the codegen could rely on injectdestructors doing its job properly + # and then the analysis step would not be required. + discard "result init optimized out" + else: + initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) elif sfConstructor in prc.flags: fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 626b1a886315..f0262f5280ed 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -271,10 +271,13 @@ sub/mmain.idx""", context check execCmdEx(cmd) == ("12\n", 0) block: # bug #15316 - let file = testsDir / "misc/m15316.nim" - let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}" - check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " & - "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1) + when not defined(windows): + # This never worked reliably on Windows. Needs further investigation but it is hard to reproduce. + # Looks like a mild stack corruption when bailing out of nested exception handling. + let file = testsDir / "misc/m15316.nim" + let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " & + "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1) block: # config.nims, nim.cfg, hintConf, bug #16557 From 325341866f6b82cba5d81db8e39ca98b0d96fd4d Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Wed, 13 Sep 2023 19:43:25 +0100 Subject: [PATCH 271/347] Make capacity work with refc [backport] (#22697) followup of #19771. --- lib/system/seqs_v2.nim | 7 ++----- lib/system/strs_v2.nim | 8 ++------ lib/system/sysstr.nim | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index c3e48019c701..9cf4032296a2 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -183,8 +183,6 @@ proc newSeq[T](s: var seq[T], len: Natural) = proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerproc, inline.} = result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p -template capacityImpl(sek: NimSeqV2): int = - if sek.p != nil: (sek.p.cap and not strlitFlag) else: 0 func capacity*[T](self: seq[T]): int {.inline.} = ## Returns the current capacity of the seq. @@ -194,9 +192,8 @@ func capacity*[T](self: seq[T]): int {.inline.} = lst.add "Nim" assert lst.capacity == 42 - {.cast(noSideEffect).}: - let sek = unsafeAddr self - result = capacityImpl(cast[ptr NimSeqV2[T]](sek)[]) + let sek = cast[ptr NimSeqV2[T]](unsafeAddr self) + result = if sek.p != nil: (sek.p.cap and not strlitFlag) else: 0 {.pop.} # See https://github.com/nim-lang/Nim/issues/21401 diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index e161172b24af..a9a104d3d17e 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -202,9 +202,6 @@ proc prepareMutation*(s: var string) {.inline.} = nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[]) -template capacityImpl(str: NimStringV2): int = - if str.p != nil: str.p.cap else: 0 - func capacity*(self: string): int {.inline.} = ## Returns the current capacity of the string. # See https://github.com/nim-lang/RFCs/issues/460 @@ -213,6 +210,5 @@ func capacity*(self: string): int {.inline.} = str.add "Nim" assert str.capacity == 42 - {.cast(noSideEffect).}: - let str = unsafeAddr self - result = capacityImpl(cast[ptr NimStringV2](str)[]) + let str = cast[ptr NimStringV2](unsafeAddr self) + result = if str.p != nil: str.p.cap else: 0 diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index b8dc7101d3a8..4acf1780b956 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -340,3 +340,25 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. result = s zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize) result.len = newLen + +func capacity*(self: string): int {.inline.} = + ## Returns the current capacity of the string. + # See https://github.com/nim-lang/RFCs/issues/460 + runnableExamples: + var str = newStringOfCap(cap = 42) + str.add "Nim" + assert str.capacity == 42 + + let str = cast[NimString](self) + result = if str != nil: str.reserved else: 0 + +func capacity*[T](self: seq[T]): int {.inline.} = + ## Returns the current capacity of the seq. + # See https://github.com/nim-lang/RFCs/issues/460 + runnableExamples: + var lst = newSeqOfCap[string](cap = 42) + lst.add "Nim" + assert lst.capacity == 42 + + let sek = cast[PGenericSeq](self) + result = if sek != nil: (sek.reserved and not strlitFlag) else: 0 From ac1804aba665b34a01cb014183f8fff0ba6db738 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Thu, 14 Sep 2023 07:22:22 +0300 Subject: [PATCH 272/347] refactor semtempl ident declarations, some special word use (#22693) `semtempl` is refactored to combine the uses of `getIdentNode`, `onlyReplaceParams`, `isTemplParam` and most of `replaceIdentBySym` into a single `getIdentReplaceParams` proc. This might fix possible problems with injections of `nkAccQuoted`. Some special word comparison in `ast` and `semtempl` are also made more efficient. --- compiler/ast.nim | 4 +- compiler/semtempl.nim | 104 +++++++++++++++++------------------------- 2 files changed, 44 insertions(+), 64 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 8658251e52a6..dd7561264a80 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - lineinfos, hashes, options, ropes, idents, int128, tables + lineinfos, hashes, options, ropes, idents, int128, tables, wordrecg from strutils import toLowerAscii when defined(nimPreviewSlimSystem): @@ -2042,7 +2042,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool = result = false proc isInfixAs*(n: PNode): bool = - return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as" + return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs) proc skipColon*(n: PNode): PNode = result = n diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index eec02812226e..3256b8d85783 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -136,24 +136,32 @@ type noGenSym: int inTemplateHeader: int -proc getIdentNode(c: var TemplCtx, n: PNode): PNode = +proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} = + result = s.kind == skParam and + s.owner == c.owner and sfTemplateParam in s.flags + +proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] = case n.kind - of nkPostfix: result = getIdentNode(c, n[1]) - of nkPragmaExpr: result = getIdentNode(c, n[0]) + of nkPostfix: result = getIdentReplaceParams(c, n[1]) + of nkPragmaExpr: result = getIdentReplaceParams(c, n[0]) of nkIdent: - result = n + result = (n, false) let s = qualifiedLookUp(c.c, n, {}) - if s != nil: - if s.owner == c.owner and s.kind == skParam: - result = newSymNode(s, n.info) - of nkAccQuoted, nkSym: result = n + if s != nil and isTemplParam(c, s): + n = newSymNode(s, n.info) + result = (n, true) + of nkSym: + result = (n, isTemplParam(c, n.sym)) + of nkAccQuoted: + result = (n, false) + for i in 0..<n.safeLen: + let (ident, hasParam) = getIdentReplaceParams(c, n[i]) + if hasParam: + result.node[i] = ident + result.hasParam = true else: illFormedAst(n, c.c.config) - result = n - -proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = - result = n.kind == nkSym and n.sym.kind == skParam and - n.sym.owner == c.owner and sfTemplateParam in n.sym.flags + result = (n, false) proc semTemplBody(c: var TemplCtx, n: PNode): PNode @@ -168,19 +176,6 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode = result = semTemplBody(c, n) closeScope(c) -proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = - result = n - if n.kind == nkIdent: - let s = qualifiedLookUp(c.c, n, {}) - if s != nil: - if s.owner == c.owner and s.kind == skParam: - incl(s.flags, sfUsed) - result = newSymNode(s, n.info) - onUse(n.info, s) - else: - for i in 0..<n.safeLen: - result[i] = onlyReplaceParams(c, n[i]) - proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym = result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info) incl(result.flags, sfGenSym) @@ -192,24 +187,10 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = k == skField: # even if injected, don't produce a sym choice here: #n = semTemplBody(c, n) - var x = n - while true: - case x.kind - of nkPostfix: x = x[1] - of nkPragmaExpr: x = x[0] - of nkIdent: break - of nkAccQuoted: - # consider: type `T TemplParam` {.inject.} - # it suffices to return to treat it like 'inject': - n = onlyReplaceParams(c, n) - return - else: - illFormedAst(x, c.c.config) - let ident = getIdentNode(c, x) - if not isTemplParam(c, ident): - if k != skField: c.toInject.incl(x.ident.id) - else: - replaceIdentBySym(c.c, n, ident) + let (ident, hasParam) = getIdentReplaceParams(c, n) + if not hasParam: + if k != skField: + c.toInject.incl(considerQuotedIdent(c.c, ident).id) else: if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma): let pragmaNode = n[1] @@ -219,15 +200,15 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = var found = false if ni.kind == nkIdent: for a in templatePragmas: - if ni.ident == getIdent(c.c.cache, $a): + if ni.ident.id == ord(a): found = true break if not found: openScope(c) pragmaNode[i] = semTemplBody(c, pragmaNode[i]) closeScope(c) - let ident = getIdentNode(c, n) - if not isTemplParam(c, ident): + let (ident, hasParam) = getIdentReplaceParams(c, n) + if not hasParam: if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)): let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) @@ -236,8 +217,6 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = replaceIdentBySym(c.c, n, newSymNode(local, n.info)) if k == skParam and c.inTemplateHeader > 0: local.flags.incl sfTemplateParam - else: - replaceIdentBySym(c.c, n, ident) proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = incl(s.flags, sfUsed) @@ -293,20 +272,21 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = result = n checkSonsLen(n, bodyPos + 1, c.c.config) - # routines default to 'inject': - if n.kind notin nkLambdaKinds and symBinding(n[pragmasPos]) == spGenSym: - let ident = getIdentNode(c, n[namePos]) - if not isTemplParam(c, ident): - var s = newGenSym(k, ident, c) - s.ast = n - addPrelimDecl(c.c, s) - styleCheckDef(c.c, n.info, s) - onDef(n.info, s) - n[namePos] = newSymNode(s, n[namePos].info) + if n.kind notin nkLambdaKinds: + # routines default to 'inject': + if symBinding(n[pragmasPos]) == spGenSym: + let (ident, hasParam) = getIdentReplaceParams(c, n[namePos]) + if not hasParam: + var s = newGenSym(k, ident, c) + s.ast = n + addPrelimDecl(c.c, s) + styleCheckDef(c.c, n.info, s) + onDef(n.info, s) + n[namePos] = newSymNode(s, n[namePos].info) + else: + n[namePos] = ident else: - n[namePos] = ident - else: - n[namePos] = semRoutineInTemplName(c, n[namePos]) + n[namePos] = semRoutineInTemplName(c, n[namePos]) # open scope for parameters openScope(c) for i in patternPos..paramsPos-1: From 96e1949610bc805af451903ba5cc4d483f7b4dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Thu, 14 Sep 2023 16:37:30 +0100 Subject: [PATCH 273/347] implements RFC: [C++] Constructors as default initializers (#22694) Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/ccgstmts.nim | 15 ++++--- compiler/ccgtypes.nim | 18 ++++++++- compiler/cgen.nim | 12 ++++-- compiler/modulegraphs.nim | 3 +- compiler/semstmts.nim | 79 +++++++++++++++++++++++-------------- tests/cpp/tinitializers.nim | 33 ++++++++++++++++ 6 files changed, 119 insertions(+), 41 deletions(-) create mode 100644 tests/cpp/tinitializers.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index d6220afbf47c..de358e24260b 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -289,14 +289,17 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) = #echo "New code produced for ", v.name.s, " ", p.config $ value.info genBracedInit(p, value, isConst = false, v.typ, result) -proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) = - var params = newRopeAppender() +proc genCppParamsForCtor(p: BProc; call: PNode): string = + result = "" var argsCounter = 0 - let typ = skipTypes(value[0].typ, abstractInst) + let typ = skipTypes(call[0].typ, abstractInst) assert(typ.kind == tyProc) - for i in 1..<value.len: + for i in 1..<call.len: assert(typ.len == typ.n.len) - genOtherArg(p, value, i, typ, params, argsCounter) + genOtherArg(p, call, i, typ, result, argsCounter) + +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) = + let params = genCppParamsForCtor(p, call) if params.len == 0: decl = runtimeFormat("$#;\n", [decl]) else: @@ -358,7 +361,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var decl = localVarDecl(p, vn) var tmp: TLoc if isCppCtorCall: - genCppVarForCtor(p, v, vn, value, decl) + genCppVarForCtor(p, value, decl) line(p, cpsStmts, decl) else: tmp = initLocExprSingleUse(p, value) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 87dcb02ad59e..1aed8442bf24 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -656,6 +656,21 @@ proc hasCppCtor(m: BModule; typ: PType): bool = if sfConstructor in prc.flags: return true +proc genCppParamsForCtor(p: BProc; call: PNode): string + +proc genCppInitializer(m: BModule, prc: BProc; typ: PType): string = + #To avoid creating a BProc per test when called inside a struct nil BProc is allowed + result = "{}" + if typ.itemId in m.g.graph.initializersPerType: + let call = m.g.graph.initializersPerType[typ.itemId] + if call != nil: + var p = prc + if p == nil: + p = BProc(module: m) + result = "{" & genCppParamsForCtor(p, call) & "}" + if prc == nil: + assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks" + proc genRecordFieldsAux(m: BModule; n: PNode, rectype: PType, check: var IntSet; result: var Rope; unionPrefix = "") = @@ -721,7 +736,8 @@ proc genRecordFieldsAux(m: BModule; n: PNode, # don't use fieldType here because we need the # tyGenericInst for C++ template support if fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ): - result.addf("\t$1$3 $2{};$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) + var initializer = genCppInitializer(m, nil, fieldType) + result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer]) else: result.addf("\t$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias]) else: internalError(m.config, n.info, "genRecordFieldsAux()") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2197947bfdfe..93059600a786 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -551,7 +551,8 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, dkVar), result.r]) + linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, + genCppInitializer(p.module, p, t)]) else: linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) constructLoc(p, result, not needsInit) @@ -606,7 +607,10 @@ proc assignLocalVar(p: BProc, n: PNode) = # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! let nl = if optLineDir in p.config.options: "" else: "\n" - let decl = localVarDecl(p, n) & (if p.module.compileToCpp and isOrHasImportedCppType(n.typ): "{};" else: ";") & nl + var decl = localVarDecl(p, n) + if p.module.compileToCpp and isOrHasImportedCppType(n.typ): + decl.add genCppInitializer(p.module, p, n.typ) + decl.add ";" & nl line(p, cpsLocals, decl) include ccgthreadvars @@ -640,7 +644,7 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = else: decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) -proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) = let s = vn.sym @@ -650,7 +654,7 @@ proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) = let td = getTypeDesc(p.module, vn.sym.typ, dkVar) genGlobalVarDecl(p, vn, td, "", decl) decl.add " " & $s.loc.r - genCppVarForCtor(p, v, vn, value, decl) + genCppVarForCtor(p, value, decl) p.module.s[cfsVars].add decl proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index b9833345357d..baedad0af46f 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -79,7 +79,8 @@ type procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId. attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc. methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods - memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual and ctor so far) + memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far). + initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only) enumToStringProcs*: Table[ItemId, LazySym] emittedTypeInfo*: Table[string, FileIndex] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5a72632efc22..006d695152a2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2077,6 +2077,53 @@ proc finishMethod(c: PContext, s: PSym) = if hasObjParam(s): methodDef(c.graph, c.idgen, s) +proc semCppMember(c: PContext; s: PSym; n: PNode) = + if sfImportc notin s.flags: + let isVirtual = sfVirtual in s.flags + let isCtor = sfConstructor in s.flags + let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member" + if c.config.backend == backendCpp: + if s.typ.len < 2 and not isCtor: + localError(c.config, n.info, pragmaName & " must have at least one parameter") + for son in s.typ: + if son!=nil and son.isMetaType: + localError(c.config, n.info, pragmaName & " unsupported for generic routine") + var typ: PType + if isCtor: + typ = s.typ[0] + if typ == nil or typ.kind != tyObject: + localError(c.config, n.info, "constructor must return an object") + else: + typ = s.typ[1] + if typ.kind == tyPtr and not isCtor: + typ = typ[0] + if typ.kind != tyObject: + localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.") + if typ.owner.id == s.owner.id and c.module.id == s.owner.id: + c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s + else: + localError(c.config, n.info, + pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope") + else: + localError(c.config, n.info, pragmaName & " procs are only supported in C++") + else: + var typ = s.typ[0] + if typ != nil and typ.kind == tyObject and typ.itemId notin c.graph.initializersPerType: + var initializerCall = newTree(nkCall, newSymNode(s)) + var isInitializer = n[paramsPos].len > 1 + for i in 1..<n[paramsPos].len: + let p = n[paramsPos][i] + let val = p[^1] + if val.kind == nkEmpty: + isInitializer = false + break + var j = 0 + while p[j].sym.kind == skParam: + initializerCall.add val + inc j + if isInitializer: + c.graph.initializersPerType[typ.itemId] = initializerCall + proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = if s.isGenericRoutine: let tt = s.typ @@ -2294,35 +2341,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if sfBorrow in s.flags and c.config.cmd notin cmdDocLike: result[bodyPos] = c.graph.emptyNode - if sfCppMember * s.flags != {} and sfImportc notin s.flags: - let isVirtual = sfVirtual in s.flags - let isCtor = sfConstructor in s.flags - let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member" - if c.config.backend == backendCpp: - if s.typ.len < 2 and not isCtor: - localError(c.config, n.info, pragmaName & " must have at least one parameter") - for son in s.typ: - if son!=nil and son.isMetaType: - localError(c.config, n.info, pragmaName & " unsupported for generic routine") - var typ: PType - if isCtor: - typ = s.typ[0] - if typ == nil or typ.kind != tyObject: - localError(c.config, n.info, "constructor must return an object") - else: - typ = s.typ[1] - if typ.kind == tyPtr and not isCtor: - typ = typ[0] - if typ.kind != tyObject: - localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.") - if typ.owner.id == s.owner.id and c.module.id == s.owner.id: - c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s - else: - localError(c.config, n.info, - pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope") - else: - localError(c.config, n.info, pragmaName & " procs are only supported in C++") - + if sfCppMember * s.flags != {}: + semCppMember(c, s, n) + if n[bodyPos].kind != nkEmpty and sfError notin s.flags: # for DLL generation we allow sfImportc to have a body, for use in VM if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not diff --git a/tests/cpp/tinitializers.nim b/tests/cpp/tinitializers.nim new file mode 100644 index 000000000000..868cf825ca86 --- /dev/null +++ b/tests/cpp/tinitializers.nim @@ -0,0 +1,33 @@ +discard """ + targets: "cpp" +""" + +{.emit:"""/*TYPESECTION*/ +struct CppStruct { + CppStruct(int x, char* y): x(x), y(y){} + void doSomething() {} + int x; + char* y; +}; +""".} +type + CppStruct {.importcpp, inheritable.} = object + ChildStruct = object of CppStruct + HasCppStruct = object + cppstruct: CppStruct + +proc constructCppStruct(a:cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "CppStruct(@)", constructor.} +proc doSomething(this: CppStruct) {.importcpp.} +proc returnCppStruct(): CppStruct = discard +proc initChildStruct: ChildStruct = ChildStruct() +proc makeChildStruct(): ChildStruct {.constructor:"""ChildStruct(): CppStruct(5, "10")""".} = discard +proc initHasCppStruct(x: cint): HasCppStruct = + HasCppStruct(cppstruct: constructCppStruct(x)) + +proc main = + var hasCppStruct = initHasCppStruct(2) #generates cppstruct = { 10 } inside the struct + hasCppStruct.cppstruct.doSomething() + discard returnCppStruct() #generates result = { 10 } + discard initChildStruct() #generates ChildStruct temp ({}) bypassed with makeChildStruct + (proc (s:CppStruct) = discard)(CppStruct()) #CppStruct temp ({10}) +main() \ No newline at end of file From 38b58239e882eaa905bafa49237f0b9ca9d43569 Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Thu, 14 Sep 2023 16:38:33 +0100 Subject: [PATCH 274/347] followup of #22568 (#22690) --- lib/system/sysstr.nim | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 4acf1780b956..54d0f2b2f490 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -47,27 +47,22 @@ else: template allocStrNoInit(size: untyped): untyped = cast[NimString](newObjNoInit(addr(strDesc), size)) -proc rawNewStringNoInit(space: int): NimString {.compilerproc.} = - var s = space - if s < 7: s = 7 +proc rawNewStringNoInit(space: int): NimString = + let s = max(space, 7) result = allocStrNoInit(sizeof(TGenericSeq) + s + 1) result.reserved = s - result.len = 0 when defined(gogc): result.elemSize = 1 proc rawNewString(space: int): NimString {.compilerproc.} = - var s = space - if s < 7: s = 7 - result = allocStr(sizeof(TGenericSeq) + s + 1) - result.reserved = s + result = rawNewStringNoInit(space) result.len = 0 - when defined(gogc): - result.elemSize = 1 + result.data[0] = '\0' proc mnewString(len: int): NimString {.compilerproc.} = - result = rawNewString(len) + result = rawNewStringNoInit(len) result.len = len + zeroMem(addr result.data[0], len + 1) proc copyStrLast(s: NimString, start, last: int): NimString {.compilerproc.} = # This is not used by most recent versions of the compiler anymore, but @@ -75,13 +70,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerproc.} = let start = max(start, 0) if s == nil: return nil let len = min(last, s.len-1) - start + 1 - if len > 0: - result = rawNewStringNoInit(len) - result.len = len - copyMem(addr(result.data), addr(s.data[start]), len) - result.data[len] = '\0' - else: - result = rawNewString(len) + result = rawNewStringNoInit(len) + result.len = len + copyMem(addr(result.data), addr(s.data[start]), len) + result.data[len] = '\0' proc copyStr(s: NimString, start: int): NimString {.compilerproc.} = # This is not used by most recent versions of the compiler anymore, but @@ -96,7 +88,8 @@ proc nimToCStringConv(s: NimString): cstring {.compilerproc, nonReloadable, inli proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} = result = rawNewStringNoInit(len) result.len = len - copyMem(addr(result.data), str, len + 1) + copyMem(addr(result.data), str, len) + result.data[len] = '\0' proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = if str == nil: NimString(nil) @@ -201,7 +194,7 @@ proc addChar(s: NimString, c: char): NimString = proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = if dest == nil: - result = rawNewStringNoInit(addlen) + result = rawNewString(addlen) elif dest.len + addlen <= dest.space: result = dest else: # slow path: @@ -227,15 +220,15 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = let n = max(newLen, 0) if s == nil: - result = mnewString(newLen) + result = mnewString(n) elif n <= s.space: result = s else: - let sp = max(resize(s.space), newLen) + let sp = max(resize(s.space), n) result = rawNewStringNoInit(sp) result.len = s.len - copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1) - zeroMem(addr result.data[s.len], newLen - s.len) + copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len) + zeroMem(addr result.data[s.len], n - s.len) result.reserved = sp result.len = n result.data[n] = '\0' From ae0a3f65c6a71c2fe50ac64549f1a6827949744d Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Thu, 14 Sep 2023 15:43:45 -0300 Subject: [PATCH 275/347] Fix Bisect bot (#22703) - https://github.com/nim-lang/Nim/actions/runs/6187256704/job/16796720625#step:4:29 - https://github.com/nim-lang/Nim/issues/22699 --- .github/workflows/bisects.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml index 362c7315805c..066ebc80b3dd 100644 --- a/.github/workflows/bisects.yml +++ b/.github/workflows/bisects.yml @@ -16,7 +16,9 @@ jobs: nim-version: 'devel' - name: Install Dependencies - run: sudo apt-get install --no-install-recommends -yq valgrind + run: | + sudo apt-get -yq update + sudo apt-get install --no-install-recommends -yq valgrind - uses: juancarlospaco/nimrun-action@nim with: From cd0d0ca5304528e33eb10e87ea27936f38bfea1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Fri, 15 Sep 2023 11:08:41 +0100 Subject: [PATCH 276/347] Document C++ Initializers (#22704) Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- doc/manual_experimental.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 72e883cbc3fd..dd28fa67c156 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2382,6 +2382,33 @@ In the example above `CppClass` has a deleted default constructor. Notice how by Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized. +Constructor Initializer +======================= + +By default Nim initializes `importcpp` types with `{}`. This can be problematic when importing +types with a deleted default constructor. In order to avoid this, one can specify default values for a constructor by specifying default values for the proc params in the `constructor` proc. + +For example: + +```nim + +{.emit: """/*TYPESECTION*/ +struct CppStruct { + CppStruct(int x, char* y): x(x), y(y){} + int x; + char* y; +}; +""".} +type + CppStruct {.importcpp, inheritable.} = object + +proc makeCppStruct(a: cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "CppStruct(@)", constructor.} + +(proc (s: CppStruct) = echo "hello")(makeCppStruct()) +# If one removes a default value from the constructor and passes it to the call explicitly, the C++ compiler will complain. + +``` + Member pragma ============= From 8836207a4e68c177d5059131df05a9d433dd3c8d Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Sat, 16 Sep 2023 10:16:12 +0300 Subject: [PATCH 277/347] implement semgnrc for tuple and object type nodes (#22709) fixes #22699 --- compiler/semgnrc.nim | 43 +++++++++++++++++++++++++++++++++-- tests/generics/mtypenodes.nim | 6 +++++ tests/generics/timports.nim | 6 ++++- 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/generics/mtypenodes.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index c8eda9c37db4..aa05f8d85d70 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -457,8 +457,47 @@ proc semGenericStmt(c: PContext, n: PNode, of nkIdent: a = n[i] else: illFormedAst(n, c.config) addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c)) - of nkObjectTy, nkTupleTy, nkTupleClassTy: - discard + of nkTupleTy: + for i in 0..<n.len: + var a = n[i] + case a.kind: + of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue + of nkIdentDefs: + checkMinSonsLen(a, 3, c.config) + a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx) + a[^1] = semGenericStmt(c, a[^1], flags, ctx) + for j in 0..<a.len-2: + addTempDecl(c, getIdentNode(c, a[j]), skField) + else: + illFormedAst(a, c.config) + of nkObjectTy: + if n.len > 0: + openScope(c) + for i in 0..<n.len: + result[i] = semGenericStmt(c, n[i], flags, ctx) + closeScope(c) + of nkRecList: + for i in 0..<n.len: + var a = n[i] + case a.kind: + of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue + of nkIdentDefs: + checkMinSonsLen(a, 3, c.config) + a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx) + a[^1] = semGenericStmt(c, a[^1], flags, ctx) + for j in 0..<a.len-2: + addTempDecl(c, getIdentNode(c, a[j]), skField) + of nkRecCase, nkRecWhen: + n[i] = semGenericStmt(c, a, flags, ctx) + else: + illFormedAst(a, c.config) + of nkRecCase: + checkSonsLen(n[0], 3, c.config) + n[0][^2] = semGenericStmt(c, n[0][^2], flags+{withinTypeDesc}, ctx) + n[0][^1] = semGenericStmt(c, n[0][^1], flags, ctx) + addTempDecl(c, getIdentNode(c, n[0][0]), skField) + for i in 1..<n.len: + n[i] = semGenericStmt(c, n[i], flags, ctx) of nkFormalParams: checkMinSonsLen(n, 1, c.config) for i in 1..<n.len: diff --git a/tests/generics/mtypenodes.nim b/tests/generics/mtypenodes.nim new file mode 100644 index 000000000000..e1132241bf5f --- /dev/null +++ b/tests/generics/mtypenodes.nim @@ -0,0 +1,6 @@ +# issue #22699 + +type Private = distinct int + +proc chop*[T](x: int): int = + cast[int](cast[tuple[field: Private]](x)) diff --git a/tests/generics/timports.nim b/tests/generics/timports.nim index 6b71cb6d3b44..e252ad19477b 100644 --- a/tests/generics/timports.nim +++ b/tests/generics/timports.nim @@ -7,7 +7,7 @@ false ''' """ -import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc +import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc, mtypenodes block tbind_bracket: @@ -57,3 +57,7 @@ block tmodule_same_as_proc: proc test[T](t: T) = mmodule_same_as_proc"a" test(0) + +block ttypenodes: + # issue #22699 + doAssert chop[bool](42) == 42 From fcf4c1ae172080b8ef00b173977c223836bdebf2 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sun, 17 Sep 2023 20:03:43 +0200 Subject: [PATCH 278/347] Fix #22713: Make size unknown for tyForward (#22714) Close #22713 --------- Co-authored-by: SirOlaf <> --- compiler/sizealignoffsetimpl.nim | 7 +++---- tests/pragmas/t22713.nim | 12 ++++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 tests/pragmas/t22713.nim diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 99e4342bbb89..424d7450f811 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -433,10 +433,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.paddingAtEnd = typ.base.paddingAtEnd of tyForward: - # is this really illegal recursion, or maybe just unknown? - typ.size = szIllegalRecursion - typ.align = szIllegalRecursion - typ.paddingAtEnd = szIllegalRecursion + typ.size = szUnknownSize + typ.align = szUnknownSize + typ.paddingAtEnd = szUnknownSize of tyStatic: if typ.n != nil: diff --git a/tests/pragmas/t22713.nim b/tests/pragmas/t22713.nim new file mode 100644 index 000000000000..3d3384632fca --- /dev/null +++ b/tests/pragmas/t22713.nim @@ -0,0 +1,12 @@ +import std/macros + + +template myPragma(x: int) {.pragma.} + +type + A = object + x: int64 + + B {.myPragma(sizeof(A)).} = object + +doAssert B.getCustomPragmaVal(myPragma) == 8 \ No newline at end of file From 5f9038a5d76847ebb922dc34c3333879e8160426 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Mon, 18 Sep 2023 07:39:22 +0300 Subject: [PATCH 279/347] make expressions opt in to symchoices (#22716) refs #22605 Sym choice nodes are now only allowed to pass through semchecking if contexts ask for them to (with `efAllowSymChoice`). Otherwise they are resolved or treated as ambiguous. The contexts that can receive symchoices in this PR are: * Call operands and addresses and emulations of such, which will subject them to overload resolution which will resolve them or fail. * Type conversion operands only for routine symchoices for type disambiguation syntax (like `(proc (x: int): int)(foo)`), which will resolve them or fail. * Proc parameter default values both at the declaration and during generic instantiation, which undergo type narrowing and so will resolve them or fail. This means unless these contexts mess up sym choice nodes should never leave the semchecking stage. This serves as a blueprint for future improvements to intermediate symbol resolution. Some tangential changes are also in this PR: 1. The `AmbiguousEnum` hint is removed, it was always disabled by default and since #22606 it only started getting emitted after the symchoice was soundly resolved. 2. Proc setter syntax (`a.b = c` becoming `` `b=`(a, c) ``) used to fully type check the RHS before passing the transformed call node to proc overloading. Now it just passes the original node directly so proc overloading can deal with its typechecking. --- compiler/condsyms.nim | 1 - compiler/lineinfos.nim | 2 - compiler/semdata.nim | 1 + compiler/semexprs.nim | 99 ++++++++++++++---------------- compiler/seminst.nim | 4 +- compiler/semtypes.nim | 2 +- config/nim.cfg | 4 -- tests/enum/tambiguousoverloads.nim | 2 +- tests/errmsgs/t8064.nim | 3 +- tests/lookups/tambprocvar.nim | 2 +- tests/specialops/tsetter.nim | 10 +++ 11 files changed, 66 insertions(+), 64 deletions(-) create mode 100644 tests/specialops/tsetter.nim diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 638cb5c1efa6..81081fd93bb1 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -143,7 +143,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasTemplateRedefinitionPragma") defineSymbol("nimHasCstringCase") defineSymbol("nimHasCallsitePragma") - defineSymbol("nimHasAmbiguousEnumHint") defineSymbol("nimHasWarnCastSizes") # deadcode defineSymbol("nimHasOutParams") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 12495aa22fd4..7cc9c68f5dd3 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -104,7 +104,6 @@ type hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency", hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace", hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro", - hintAmbiguousEnum = "AmbiguousEnum", hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext", hintMsgOrigin = "MsgOrigin", # since 1.3.5 hintDeclaredLoc = "DeclaredLoc", # since 1.5.1 @@ -225,7 +224,6 @@ const hintGCStats: "$1", hintGlobalVar: "global variable declared here", hintExpandMacro: "expanded macro: $1", - hintAmbiguousEnum: "$1", hintUser: "$1", hintUserRaw: "$1", hintExtendedContext: "$1", diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 9386c3763f13..32e557f1860f 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -76,6 +76,7 @@ type efNoDiagnostics, efTypeAllowed # typeAllowed will be called after efWantNoDefaults + efAllowSymChoice # symchoice node should not be resolved TExprFlags* = set[TExprFlag] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d62e2561050b..590d2610be92 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -52,7 +52,7 @@ template rejectEmptyNode(n: PNode) = proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = rejectEmptyNode(n) # same as 'semExprWithType' but doesn't check for proc vars - result = semExpr(c, n, flags + {efOperand}) + result = semExpr(c, n, flags + {efOperand, efAllowSymChoice}) if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}): @@ -90,42 +90,10 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType # do not produce another redundant error message: result = errorNode(c, n) -proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode = - let first = n[0].sym - var foundSym: PSym = nil - if first.kind == skEnumField and - not isAmbiguous(c, first.name, {skEnumField}, foundSym) and - foundSym == first: - # choose the first resolved enum field, i.e. the latest in scope - # to mirror behavior before overloadable enums - if hintAmbiguousEnum in c.config.notes: - var err = "ambiguous enum field '" & first.name.s & - "' assumed to be of type " & typeToString(first.typ) & - " -- use one of the following:\n" - for child in n: - let candidate = child.sym - err.add " " & candidate.owner.name.s & "." & candidate.name.s & "\n" - message(c.config, orig.info, hintAmbiguousEnum, err) - result = n[0] - else: - var err = "ambiguous identifier '" & first.name.s & - "' -- use one of the following:\n" - for child in n: - let candidate = child.sym - err.add " " & candidate.owner.name.s & "." & candidate.name.s - err.add ": " & typeToString(candidate.typ) & "\n" - localError(c.config, orig.info, err) - n.typ = errorType(c) - result = n - proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType) if result.typ == nil and efInTypeof in flags: result.typ = c.voidType - elif (result.typ == nil or result.typ.kind == tyNone) and - efTypeAllowed in flags and - result.kind == nkClosedSymChoice and result.len > 0: - result = ambiguousSymChoice(c, n, result) elif result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) @@ -158,6 +126,39 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) +proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode + +proc isSymChoice(n: PNode): bool {.inline.} = + result = n.kind in nkSymChoices + +proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + result = n + if expectedType != nil: + result = fitNode(c, expectedType, result, n.info) + if isSymChoice(result) and efAllowSymChoice notin flags: + # some contexts might want sym choices preserved for later disambiguation + # in general though they are ambiguous + let first = n[0].sym + var foundSym: PSym = nil + if first.kind == skEnumField and + not isAmbiguous(c, first.name, {skEnumField}, foundSym) and + foundSym == first: + # choose the first resolved enum field, i.e. the latest in scope + # to mirror behavior before overloadable enums + result = n[0] + else: + var err = "ambiguous identifier '" & first.name.s & + "' -- use one of the following:\n" + for child in n: + let candidate = child.sym + err.add " " & candidate.owner.name.s & "." & candidate.name.s + err.add ": " & typeToString(candidate.typ) & "\n" + localError(c.config, n.info, err) + n.typ = errorType(c) + result = n + if result.kind == nkSym: + result = semSym(c, result, result.sym, flags) + proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} = result = copyTree(s.astdef) if result.isNil: @@ -297,9 +298,6 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = if result and src.kind == tyNil: return dst.size <= conf.target.ptrSize -proc isSymChoice(n: PNode): bool {.inline.} = - result = n.kind in nkSymChoices - proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = # XXX: liftParamType started to perform addDecl # we could do that instead in semTypeNode by snooping for added @@ -361,10 +359,10 @@ proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType if n[1].kind == nkExprEqExpr and targetType.skipTypes(abstractPtrs).kind == tyObject: localError(c.config, n.info, "object construction uses ':', not '='") - var op = semExprWithType(c, n[1], flags * {efDetermineType}) - if op.kind == nkClosedSymChoice and op.len > 0 and - op[0].sym.kind == skEnumField: # resolves overloadedable enums - op = ambiguousSymChoice(c, n, op) + var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice}) + if isSymChoice(op) and op[0].sym.kind notin routineKinds: + # T(foo) disambiguation syntax only allowed for routines + op = semSymChoice(c, op) if targetType.kind != tyGenericParam and targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) result.add final @@ -1057,7 +1055,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType else: n[0] = n0 else: - n[0] = semExpr(c, n[0], {efInCall}) + n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice}) let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) @@ -1464,7 +1462,8 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode = onUse(n[1].info, s) return - n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable}) + # extra flags since LHS may become a call operand: + n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice}) #restoreOldStyleType(n[0]) var i = considerQuotedIdent(c, n[1], n) var ty = n[0].typ @@ -1619,7 +1618,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = return checkMinSonsLen(n, 2, c.config) # signal that generic parameters may be applied after - n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric}) + n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice}) var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) if arr.kind == tyStatic: @@ -1718,7 +1717,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # nodes? let aOrig = nOrig[0] - result = newTreeI(nkCall, n.info, setterId, a[0], semExprWithType(c, n[1])) + result = newTreeI(nkCall, n.info, setterId, a[0], n[1]) result.flags.incl nfDotSetter let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1]) result = semOverloadedCallAnalyseEffects(c, result, orig, {}) @@ -3059,10 +3058,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType result = enumFieldSymChoice(c, n, s) else: result = semSym(c, n, s, flags) - if expectedType != nil and isSymChoice(result): - result = fitNode(c, expectedType, result, n.info) - if result.kind == nkSym: - result = semSym(c, result, result.sym, flags) + if isSymChoice(result): + result = semSymChoice(c, result, flags, expectedType) + of nkClosedSymChoice, nkOpenSymChoice: + result = semSymChoice(c, result, flags, expectedType) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! @@ -3263,10 +3262,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType considerGenSyms(c, n) of nkTableConstr: result = semTableConstr(c, n, expectedType) - of nkClosedSymChoice, nkOpenSymChoice: - # handling of sym choices is context dependent - # the node is left intact for now - discard of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType) of nkAsgn, nkFastAsgn: result = semAsgn(c, n) of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 1dba1ebdc346..dc25230c20b9 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -269,7 +269,9 @@ proc instantiateProcType(c: PContext, pt: TIdTable, for i in 1..<def.len: def[i] = replaceTypeVarsN(cl, def[i], 1) - def = semExprWithType(c, def) + # allow symchoice since node will be fit later + # although expectedType should cover it + def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 282bc53fea6a..dda78c69f175 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1320,7 +1320,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, def.typ = makeTypeFromExpr(c, def.copyTree) break determineType - def = semExprWithType(c, def, {efDetermineType}, defTyp) + def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp) if def.referencesAnotherParam(getCurrOwner(c)): def.flags.incl nfDefaultRefsParam diff --git a/config/nim.cfg b/config/nim.cfg index 7a2d5c76efe5..7c99581396df 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -18,10 +18,6 @@ cc = gcc hint[LineTooLong]=off @end -@if nimHasAmbiguousEnumHint: - # not needed if hint is a style check - hint[AmbiguousEnum]=off -@end #hint[XDeclaredButNotUsed]=off threads:on diff --git a/tests/enum/tambiguousoverloads.nim b/tests/enum/tambiguousoverloads.nim index 1b0d92608585..aa75eaa91f73 100644 --- a/tests/enum/tambiguousoverloads.nim +++ b/tests/enum/tambiguousoverloads.nim @@ -9,7 +9,7 @@ block: # bug #21887 EnumC = enum C doAssert typeof(EnumC(A)) is EnumC #[tt.Error - ^ ambiguous identifier 'A' -- use one of the following: + ^ ambiguous identifier 'A' -- use one of the following: EnumA.A: EnumA EnumB.A: EnumB]# diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim index e7941e36a7f1..6be83fd1ae19 100644 --- a/tests/errmsgs/t8064.nim +++ b/tests/errmsgs/t8064.nim @@ -4,5 +4,6 @@ values discard """ - errormsg: "expression has no type: values" + # either this or "expression has no type": + errormsg: "ambiguous identifier 'values' -- use one of the following:" """ diff --git a/tests/lookups/tambprocvar.nim b/tests/lookups/tambprocvar.nim index 2a9921bada12..33323fbb2a45 100644 --- a/tests/lookups/tambprocvar.nim +++ b/tests/lookups/tambprocvar.nim @@ -16,4 +16,4 @@ block: block: let x = `+` #[tt.Error - ^ ambiguous identifier '+' -- use one of the following:]# + ^ ambiguous identifier '+' -- use one of the following:]# diff --git a/tests/specialops/tsetter.nim b/tests/specialops/tsetter.nim new file mode 100644 index 000000000000..6175cbec4c4a --- /dev/null +++ b/tests/specialops/tsetter.nim @@ -0,0 +1,10 @@ +block: # ensure RHS of setter statement is treated as call operand + proc `b=`(a: var int, c: proc (x: int): int) = + a = c(a) + + proc foo(x: int): int = x + 1 + proc foo(x: float): float = x - 1 + + var a = 123 + a.b = foo + doAssert a == 124 From bd857151d827c732f882c1fef52b91b5582ed65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Mon, 18 Sep 2023 07:26:21 +0100 Subject: [PATCH 280/347] prevents declaring a constructor without importcpp fixes #22712 (#22715) Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/semstmts.nim | 9 +++++++-- tests/cpp/t22712.nim | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/cpp/t22712.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 006d695152a2..3c3a8b72dbfe 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1712,7 +1712,10 @@ proc addThis(c: PContext, n: PNode, t: PType, owner: TSymKind) = n.add newSymNode(c.p.resultSym) addParamOrResult(c, c.p.resultSym, owner) #resolves nim's obj ctor inside cpp ctors see #22669 - s.ast = c.semExpr(c, newTree(nkCall, t[0].sym.ast[0])) + var typAst = t[0].sym.ast[0] + if typAst.kind == nkPragmaExpr: + typAst = typAst[0] + s.ast = c.semExpr(c, newTree(nkCall, typAst)) proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = template genResSym(s) = @@ -2093,6 +2096,8 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) = typ = s.typ[0] if typ == nil or typ.kind != tyObject: localError(c.config, n.info, "constructor must return an object") + if sfImportc in typ.sym.flags: + localError(c.config, n.info, "constructor in an imported type needs importcpp pragma") else: typ = s.typ[1] if typ.kind == tyPtr and not isCtor: @@ -2369,7 +2374,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # used for overload resolution (there is no instantiation of the symbol) if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) var resultType: PType - if sfConstructor in s.flags: + if {sfConstructor, sfImportc} * s.flags == {sfConstructor}: resultType = makePtrType(c, s.typ[0]) addThis(c, n, resultType, skProc) else: diff --git a/tests/cpp/t22712.nim b/tests/cpp/t22712.nim new file mode 100644 index 000000000000..34ef67ac85e5 --- /dev/null +++ b/tests/cpp/t22712.nim @@ -0,0 +1,15 @@ +discard """ +targets: "cpp" +errormsg: "constructor in an imported type needs importcpp pragma" +line: 14 +""" +{.emit: """/*TYPESECTION*/ +struct CppStruct { + CppStruct(); +}; +""".} + +type CppStruct {.importcpp.} = object + +proc makeCppStruct(): CppStruct {.constructor.} = + discard \ No newline at end of file From deefbc420e218459ffc6ccf915f625cd48e083b4 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 18 Sep 2023 14:28:40 +0800 Subject: [PATCH 281/347] fixes `result` requires explicit initialization on noReturn code (#22717) fixes #21615; fixes #16735 It also partially fixes | #22673, though It still gives 'baseless' warnings. --- compiler/sempass2.nim | 2 +- tests/init/tcompiles.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 823699a8cdbd..f542a124476e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1556,7 +1556,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = strictDefs in c.features) and s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone: var res = s.ast[resultPos].sym # get result symbol - if res.id notin t.init: + if res.id notin t.init and breaksBlock(body) != bsNoReturn: if tfRequiresInit in s.typ[0].flags: localError(g.config, body.info, "'$1' requires explicit initialization" % "result") else: diff --git a/tests/init/tcompiles.nim b/tests/init/tcompiles.nim index 2072702ad744..e86cad1e2205 100644 --- a/tests/init/tcompiles.nim +++ b/tests/init/tcompiles.nim @@ -62,3 +62,30 @@ block: raise newException(ValueError, "unreachable") discard test(true) + +# bug #21615 +# bug #16735 + +block: + type Test {.requiresInit.} = object + id: int + + proc bar(): int = + raise newException(CatchableError, "error") + + proc test(): Test = + raise newException(CatchableError, "") + + template catchError(body) = + var done = false + try: + body + except CatchableError: + done = true + doAssert done + + catchError: + echo test() + + catchError: + echo bar() From 63c2ea5566cc7a0983cc748a3d4855d24beeebda Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 18 Sep 2023 16:00:46 +0800 Subject: [PATCH 282/347] fixes incorrect cint overflow in system (#22718) fixes #22700 --- lib/std/syncio.nim | 2 +- lib/system.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index 879301f8afac..498b2b5a446d 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -246,7 +246,7 @@ when defined(windows): # machine. We also enable `setConsoleOutputCP(65001)` now by default. # But we cannot call printf directly as the string might contain \0. # So we have to loop over all the sections separated by potential \0s. - var i = c_fprintf(f, "%s", s) + var i = int c_fprintf(f, "%s", s) while i < s.len: if s[i] == '\0': let w = c_fputc('\0', f) diff --git a/lib/system.nim b/lib/system.nim index 4949430a3469..633f637ca046 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2788,7 +2788,7 @@ when notJSnotNims: # machine. We also enable `setConsoleOutputCP(65001)` now by default. # But we cannot call printf directly as the string might contain \0. # So we have to loop over all the sections separated by potential \0s. - var i = c_fprintf(f, "%s", s) + var i = int c_fprintf(f, "%s", s) while i < s.len: if s[i] == '\0': let w = c_fputc('\0', f) From 741285b335343ad61afc2d48778c52cd097d1249 Mon Sep 17 00:00:00 2001 From: litlighilit <litlighilit@foxmail.com> Date: Mon, 18 Sep 2023 19:15:17 +0800 Subject: [PATCH 283/347] Update osfiles.nim, make `moveFile` consider permission on *nix (#22719) see https://github.com/nim-lang/Nim/issues/22674 --- lib/std/private/osfiles.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index f2e7bf11d2fe..4596adfb273f 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -399,7 +399,7 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", raiseAssert "unreachable" else: # Fallback to copy & del - copyFile(source, dest, {cfSymlinkAsIs}) + copyFileWithPermissions(source, dest, options={cfSymlinkAsIs}) try: removeFile(source) except: From dba9000609ef55dfbcf7391edd2d1375f8ea3a1e Mon Sep 17 00:00:00 2001 From: sls1005 <90055573+sls1005@users.noreply.github.com> Date: Mon, 18 Sep 2023 22:42:43 +0800 Subject: [PATCH 284/347] Add descriptions and examples for `rawProc` and `rawEnv` (#22710) Add descriptions for `rawProc` and `rawEnv`. See <https://forum.nim-lang.org/t/10485> for more informations. --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> Co-authored-by: Juan Carlos <juancarlospaco@gmail.com> Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- lib/system.nim | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 633f637ca046..56fa91ee23e9 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2246,19 +2246,49 @@ when notJSnotNims: proc rawProc*[T: proc {.closure.} | iterator {.closure.}](x: T): pointer {.noSideEffect, inline.} = ## Retrieves the raw proc pointer of the closure `x`. This is - ## useful for interfacing closures with C/C++, hash compuations, etc. + ## useful for interfacing closures with C/C++, hash computations, etc. + ## If `rawEnv(x)` returns `nil`, the proc which the result points to + ## takes as many parameters as `x`, but with `{.nimcall.}` as its calling + ## convention instead of `{.closure.}`, otherwise it takes one more parameter + ## which is a `pointer`, and it still has `{.nimcall.}` as its calling convention. + ## To invoke the resulted proc, what this returns has to be casted into a `proc`, + ## not a `ptr proc`, and, in a case where `rawEnv(x)` returns non-`nil`, + ## the last and additional argument has to be the result of `rawEnv(x)`. + ## This is not available for the JS target. #[ The conversion from function pointer to `void*` is a tricky topic, but this should work at least for c++ >= c++11, e.g. for `dlsym` support. refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869, https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c ]# + runnableExamples: + proc makeClosure(x: int): (proc(y: int): int) = + var n = x + result = ( + proc(y: int): int = + n += y + return n + ) + + var + c1 = makeClosure(10) + e = c1.rawEnv() + p = c1.rawProc() + + if e.isNil(): + let c2 = cast[proc(y: int): int {.nimcall.}](p) + echo c2(2) + else: + let c3 = cast[proc(y: int; env: pointer): int {.nimcall.}](p) + echo c3(3, e) + {.emit: """ `result` = (void*)`x`.ClP_0; """.} proc rawEnv*[T: proc {.closure.} | iterator {.closure.}](x: T): pointer {.noSideEffect, inline.} = ## Retrieves the raw environment pointer of the closure `x`. See also `rawProc`. + ## This is not available for the JS target. {.emit: """ `result` = `x`.ClE_0; """.} From 2c5b94bbfd8a050ce08072222511cbb9eb20f771 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 19 Sep 2023 04:57:03 +0800 Subject: [PATCH 285/347] fixes #22692; ships `ci/funs.sh` (#22721) fixes #22692 --- compiler/installer.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/installer.ini b/compiler/installer.ini index 49ee59b0d0c5..d12127bde91c 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -71,6 +71,7 @@ Files: "testament" Files: "nimsuggest" Files: "nimsuggest/tests/*.nim" Files: "changelogs/*.md" +Files: "ci/funs.sh" [Lib] Files: "lib" From b542be1e7de067573370a3159b0812396309ef8b Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Mon, 18 Sep 2023 21:57:30 +0100 Subject: [PATCH 286/347] Fix `capacity` for const and shallow [backport] (#22705) --- lib/system.nim | 8 ++++---- lib/system/seqs_v2.nim | 2 +- lib/system/strs_v2.nim | 2 +- lib/system/sysstr.nim | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 56fa91ee23e9..8cd526bd02af 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -457,10 +457,6 @@ when not defined(js) and not defined(nimSeqsV2): data: UncheckedArray[char] NimString = ptr NimStringDesc -when notJSnotNims and not defined(nimSeqsV2): - template space(s: PGenericSeq): int {.dirty.} = - s.reserved and not (seqShallowFlag or strlitFlag) - when notJSnotNims: include "system/hti" @@ -1048,6 +1044,10 @@ const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own +when notJSnotNims and not defined(nimSeqsV2): + template space(s: PGenericSeq): int = + s.reserved and not (seqShallowFlag or strlitFlag) + when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): # tcc doesn't support TLS {.error: "`--tlsEmulation:on` must be used when using threads with tcc backend".} diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 9cf4032296a2..c4e3e3e6b1cb 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -193,7 +193,7 @@ func capacity*[T](self: seq[T]): int {.inline.} = assert lst.capacity == 42 let sek = cast[ptr NimSeqV2[T]](unsafeAddr self) - result = if sek.p != nil: (sek.p.cap and not strlitFlag) else: 0 + result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0 {.pop.} # See https://github.com/nim-lang/Nim/issues/21401 diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index a9a104d3d17e..abdbcd7c3ba2 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -211,4 +211,4 @@ func capacity*(self: string): int {.inline.} = assert str.capacity == 42 let str = cast[ptr NimStringV2](unsafeAddr self) - result = if str.p != nil: str.p.cap else: 0 + result = if str.p != nil: str.p.cap and not strlitFlag else: 0 diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 54d0f2b2f490..e4d6479ec7cf 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -343,7 +343,7 @@ func capacity*(self: string): int {.inline.} = assert str.capacity == 42 let str = cast[NimString](self) - result = if str != nil: str.reserved else: 0 + result = if str != nil: str.space else: 0 func capacity*[T](self: seq[T]): int {.inline.} = ## Returns the current capacity of the seq. @@ -354,4 +354,4 @@ func capacity*[T](self: seq[T]): int {.inline.} = assert lst.capacity == 42 let sek = cast[PGenericSeq](self) - result = if sek != nil: (sek.reserved and not strlitFlag) else: 0 + result = if sek != nil: sek.space else: 0 From 51cb493b221e704efce126049f9f320eb1cb1a36 Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Tue, 19 Sep 2023 10:14:55 +0300 Subject: [PATCH 287/347] make parseEnum skip type aliases for enum type sym (#22727) fixes #22726 --- lib/std/enumutils.nim | 3 ++- tests/stdlib/tstrutils.nim | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 0386c2589f39..1ce82eccf21b 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -22,7 +22,8 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, # a normalized string comparison to the `argSym` input. # string normalization is done using passed normalizer. let typ = typ.getTypeInst[1] - let impl = typ.getImpl[2] + let typSym = typ.getTypeImpl.getTypeInst # skip aliases etc to get type sym + let impl = typSym.getImpl[2] expectKind impl, nnkEnumTy let normalizerNode = quote: `normalizer` expectKind normalizerNode, nnkSym diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 847e226560b7..9cc65f218b0c 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -679,11 +679,22 @@ template main() = doAssert b == f2 doAssert c == f3 - block: # parseEnum TODO: merge above - type MyEnum = enum enA, enB, enC, enuD, enE - doAssert parseEnum[MyEnum]("enu_D") == enuD + block: + type MyEnum = enum enA, enB, enC, enuD, enE + doAssert parseEnum[MyEnum]("enu_D") == enuD - doAssert parseEnum("invalid enum value", enC) == enC + doAssert parseEnum("invalid enum value", enC) == enC + + block: # issue #22726 + type SomeEnum = enum A, B, C + + proc assignEnum(dest: var enum, s: string) = + type ty = typeof(dest) + dest = parseEnum[ty](s) + + var v: SomeEnum + v.assignEnum("A") + doAssert v == A block: # indentation doAssert 0 == indentation """ From 81756d1810c7c00e0bb706bb79f5437120ae4c0e Mon Sep 17 00:00:00 2001 From: metagn <metagngn@gmail.com> Date: Tue, 19 Sep 2023 10:26:26 +0300 Subject: [PATCH 288/347] second test case haul for templates and generics (#22728) closes #8390, closes #11726, closes #8446, closes #21221, closes #7461, closes #7995 --- tests/generics/tgenerics_various.nim | 7 +++++++ tests/parser/tprocexprasstmt.nim | 13 ++++++++++++- tests/statictypes/tstatictypes.nim | 21 +++++++++++++++++++++ tests/template/template_issues.nim | 4 ---- tests/template/tgenericparam.nim | 15 ++++++++++++++- tests/template/tqualifiedident.nim | 8 ++++++++ 6 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 tests/template/tqualifiedident.nim diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index aff851981d61..b6ace4e7d15a 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -231,3 +231,10 @@ doSomething(identity((1, 2))) proc myProc[T, U](x: T or U) = discard myProc[int, string](x = 2) + +block: # issue #8390 + proc x[T:SomeFloat](q: openarray[T], y: T = 1): string = + doAssert $q.type == $openarray[y.type] + $y.type + + doAssert x(@[1.0]) == $1.0.type diff --git a/tests/parser/tprocexprasstmt.nim b/tests/parser/tprocexprasstmt.nim index a02dde6f6fca..22fb4a7c8819 100644 --- a/tests/parser/tprocexprasstmt.nim +++ b/tests/parser/tprocexprasstmt.nim @@ -1,3 +1,14 @@ func r(): auto = func(): int = 2 -discard r()() +doAssert r()() == 2 + +block: # issue #11726 + let foo = block: + var x: int + proc = inc x # "identifier expected, but got '='" + + template paint(): untyped = + proc (s: string): string = s + + let s = paint() + doAssert s("abc") == "abc" diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 24c99b26bbea..572b8c4a535d 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -411,6 +411,27 @@ block: # Ensure static descriminated objects compile discard instance discard MyObject[KindC]() +block: # more cases of above, issue #8446 + type + Color = enum + red, green, blue + Blah[color: static[Color]] = object + when color == red: + a: string + else: + b: int + + proc foo(blah: Blah) = discard + foo(Blah[red](a: "abc")) + + type + Mytype[K: static[int]] = object + when K < 16: + data: uint8 + else: + data: uint64 + proc usingMyt(k: Mytype) = discard # triggers Error: cannot generate code for: K + block: # bug #22600 proc f(n: static int): int = n * 2 # same for template diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index 5b7c54ed6449..58c40941db69 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -302,7 +302,3 @@ block: # bug #21920 discard t[void]() # Error: expression has no type: discard - -block: # issue #19865 - template f() = discard default(system.int) - f() diff --git a/tests/template/tgenericparam.nim b/tests/template/tgenericparam.nim index d33f55cf70e3..becf75d360ef 100644 --- a/tests/template/tgenericparam.nim +++ b/tests/template/tgenericparam.nim @@ -36,7 +36,7 @@ block: # basic template generic parameter substitution template run[T](): T = default(T) doAssert run[int]() == 0 -import options, tables +import options, tables, typetraits block: # complex cases of above with imports block: # issue #19576, complex case @@ -78,3 +78,16 @@ block: # complex cases of above with imports else: Foo.init(A,"hi") let op = fromOption(some(5)) + block: # issue #7461 + template p[T](): untyped = none(T) + doAssert p[int]() == none(int) + block: # issue #7995 + var res: string + template copyRange[T](dest: seq[T], destOffset: int) = + when supportsCopyMem(T): + res = "A" + else: + res = "B" + var a = @[1, 2, 3] + copyRange(a, 0) + doAssert res == "A" diff --git a/tests/template/tqualifiedident.nim b/tests/template/tqualifiedident.nim new file mode 100644 index 000000000000..463b14ee7fa9 --- /dev/null +++ b/tests/template/tqualifiedident.nim @@ -0,0 +1,8 @@ +block: # issue #19865 + template f() = discard default(system.int) + f() + +# issue #21221, same as above +type M = object +template r() = discard default(tqualifiedident.M) +r() From 5568ba0d9f39469891b5e4625ad39ebcfa5e1ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Tue, 19 Sep 2023 20:16:55 +0100 Subject: [PATCH 289/347] `constructor` now uses `result` instead of `this` (#22724) --- compiler/cgen.nim | 6 +----- compiler/semstmts.nim | 36 ++++++++---------------------------- tests/cpp/tconstructor.nim | 8 ++++---- 3 files changed, 13 insertions(+), 37 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 93059600a786..e9045b0665c5 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1191,12 +1191,8 @@ proc genProcAux*(m: BModule, prc: PSym) = initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) elif sfConstructor in prc.flags: + resNode.sym.loc.flags.incl lfIndirect fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) - let ty = resNode.sym.typ[0] #generate nim's ctor - for i in 1..<resNode.sym.ast.len: - let field = resNode.sym.ast[i] - genFieldObjConstr(p, ty, useTemp = false, isRef = false, - field[0], field[1], check = nil, resNode.sym.loc, "(*this)", tmpInfo) else: fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ[0]) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3c3a8b72dbfe..75b575fd9125 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1702,21 +1702,6 @@ proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = n[i] = dNode swapResult(n[i], sRes, dNode) - -proc addThis(c: PContext, n: PNode, t: PType, owner: TSymKind) = - var s = newSym(skResult, getIdent(c.cache, "this"), c.idgen, - getCurrOwner(c), n.info) - s.typ = t - incl(s.flags, sfUsed) - c.p.resultSym = s - n.add newSymNode(c.p.resultSym) - addParamOrResult(c, c.p.resultSym, owner) - #resolves nim's obj ctor inside cpp ctors see #22669 - var typAst = t[0].sym.ast[0] - if typAst.kind == nkPragmaExpr: - typAst = typAst[0] - s.ast = c.semExpr(c, newTree(nkCall, typAst)) - proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = template genResSym(s) = var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, @@ -2373,19 +2358,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # Macros and Templates can have generic parameters, but they are only # used for overload resolution (there is no instantiation of the symbol) if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ) - var resultType: PType - if {sfConstructor, sfImportc} * s.flags == {sfConstructor}: - resultType = makePtrType(c, s.typ[0]) - addThis(c, n, resultType, skProc) - else: - maybeAddResult(c, s, n) - resultType = - if s.kind == skMacro: - sysTypeFromName(c.graph, n.info, "NimNode") - elif not isInlineIterator(s.typ): - s.typ[0] - else: - nil + maybeAddResult(c, s, n) + let resultType = + if s.kind == skMacro: + sysTypeFromName(c.graph, n.info, "NimNode") + elif not isInlineIterator(s.typ): + s.typ[0] + else: + nil # semantic checking also needed with importc in case used in VM s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], resultType)) # unfortunately we cannot skip this step when in 'system.compiles' diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim index 32fb5e1a27b6..8c5d4dca2dd2 100644 --- a/tests/cpp/tconstructor.nim +++ b/tests/cpp/tconstructor.nim @@ -46,7 +46,7 @@ type NimClassNoNarent* = object x: int32 proc makeNimClassNoParent(x:int32): NimClassNoNarent {. constructor.} = - this.x = x + result.x = x discard let nimClassNoParent = makeNimClassNoParent(1) @@ -58,11 +58,11 @@ var nimClassNoParentDef {.used.}: NimClassNoNarent #test has a default construc type NimClass* = object of CppClass proc makeNimClass(x:int32): NimClass {. constructor:"NimClass('1 #1) : CppClass(0, #1) ".} = - this.x = x + result.x = x #optinially define the default constructor so we get rid of the cpp warn and we can declare the obj (note: default constructor of 'tyObject_NimClass__apRyyO8cfRsZtsldq1rjKA' is implicitly deleted because base class 'CppClass' has no default constructor) proc makeCppClass(): NimClass {. constructor: "NimClass() : CppClass(0, 0) ".} = - this.x = 1 + result.x = 1 let nimClass = makeNimClass(1) var nimClassDef {.used.}: NimClass #since we explictly defined the default constructor we can declare the obj @@ -95,7 +95,7 @@ type else: discard proc makeNimClassWithDefault(): NimClassWithDefault {.constructor.} = - discard + result = NimClassWithDefault() proc init = for i in 0 .. 1: From fefde3a735c3bc931a4d3289e43f3b95f65e1256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Wed, 20 Sep 2023 07:19:29 +0100 Subject: [PATCH 290/347] fixes compiler crash by preventing exportc on generics (#22731) Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/semstmts.nim | 9 ++++++--- tests/types/texportgeneric.nim | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/types/texportgeneric.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 75b575fd9125..2df1c42b3d1e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1486,9 +1486,12 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # final pass if a[2].kind in nkCallKinds: incl a[2].flags, nfSem # bug #10548 - if sfExportc in s.flags and s.typ.kind == tyAlias: - localError(c.config, name.info, "{.exportc.} not allowed for type aliases") - + if sfExportc in s.flags: + if s.typ.kind == tyAlias: + localError(c.config, name.info, "{.exportc.} not allowed for type aliases") + elif s.typ.kind == tyGenericBody: + localError(c.config, name.info, "{.exportc.} not allowed for generic types") + if tfBorrowDot in s.typ.flags: let body = s.typ.skipTypes({tyGenericBody}) if body.kind != tyDistinct: diff --git a/tests/types/texportgeneric.nim b/tests/types/texportgeneric.nim new file mode 100644 index 000000000000..9e6be2bb64c1 --- /dev/null +++ b/tests/types/texportgeneric.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "{.exportc.} not allowed for generic types" + line: 6 +""" + +type Struct[T] {.exportc.} = object + a:int + b: T \ No newline at end of file From af617be67a4e038354617b272d373a6fbe583644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Wed, 20 Sep 2023 11:02:55 +0100 Subject: [PATCH 291/347] removes references to `this` in the `constructor` section (#22730) --- doc/manual_experimental.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index dd28fa67c156..d05a693bb994 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2341,11 +2341,10 @@ type Foo* = object x: int32 proc makeFoo(x: int32): Foo {.constructor.} = - this.x = x + result.x = x ``` It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. -Notice, inside the body of the constructor one has access to `this` which is of the type `ptr Foo`. No `result` variable is available. Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints. @@ -2371,11 +2370,11 @@ type NimClass* = object of CppClass proc makeNimClass(x: int32): NimClass {.constructor:"NimClass('1 #1) : CppClass(0, #1)".} = - this.x = x + result.x = x # Optional: define the default constructor explicitly proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} = - this.x = 1 + result.x = 1 ``` In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropiate constructor. From d82bc0a29fea53a172260a3b3301b90a96974274 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 20 Sep 2023 18:50:23 +0800 Subject: [PATCH 292/347] items, pairs and friends now use `unCheckedInc` (#22729) `{.push overflowChecks: off.}` works in backends. Though it could be implemented as a magic function. By inspecting the generated C code, the overflow check is eliminated in the debug or release mode. ![image](https://github.com/nim-lang/Nim/assets/43030857/49c3dbf4-675e-414a-b972-b91cf218c9f8) Likewise, the index checking is probably not needed. --- lib/system/iterators.nim | 57 ++++++++++++++++++++---------------- tests/arc/topt_no_cursor.nim | 4 +++ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 4bd12680f57d..e511f25087d6 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -8,12 +8,17 @@ when not defined(nimNoLentIterators): else: template lent2(T): untyped = T +template unCheckedInc(x) = + {.push overflowChecks: off.} + inc(x) + {.pop.} + iterator items*[T: not char](a: openArray[T]): lent2 T {.inline.} = ## Iterates over each item of `a`. var i = 0 while i < len(a): yield a[i] - inc(i) + unCheckedInc(i) iterator items*[T: char](a: openArray[T]): T {.inline.} = ## Iterates over each item of `a`. @@ -23,14 +28,14 @@ iterator items*[T: char](a: openArray[T]): T {.inline.} = var i = 0 while i < len(a): yield a[i] - inc(i) + unCheckedInc(i) iterator mitems*[T](a: var openArray[T]): var T {.inline.} = ## Iterates over each item of `a` so that you can modify the yielded value. var i = 0 while i < len(a): yield a[i] - inc(i) + unCheckedInc(i) iterator items*[IX, T](a: array[IX, T]): T {.inline.} = ## Iterates over each item of `a`. @@ -39,7 +44,7 @@ iterator items*[IX, T](a: array[IX, T]): T {.inline.} = while true: yield a[i] if i >= high(IX): break - inc(i) + unCheckedInc(i) iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} = ## Iterates over each item of `a` so that you can modify the yielded value. @@ -48,7 +53,7 @@ iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} = while true: yield a[i] if i >= high(IX): break - inc(i) + unCheckedInc(i) iterator items*[T](a: set[T]): T {.inline.} = ## Iterates over each element of `a`. `items` iterates only over the @@ -57,7 +62,7 @@ iterator items*[T](a: set[T]): T {.inline.} = var i = low(T).int while i <= high(T).int: if T(i) in a: yield T(i) - inc(i) + unCheckedInc(i) iterator items*(a: cstring): char {.inline.} = ## Iterates over each item of `a`. @@ -76,7 +81,7 @@ iterator items*(a: cstring): char {.inline.} = let n = len(a) while i < n: yield a[i] - inc(i) + unCheckedInc(i) when defined(js): impl() else: when nimvm: @@ -86,7 +91,7 @@ iterator items*(a: cstring): char {.inline.} = var i = 0 while a[i] != '\0': yield a[i] - inc(i) + unCheckedInc(i) iterator mitems*(a: var cstring): var char {.inline.} = ## Iterates over each item of `a` so that you can modify the yielded value. @@ -109,7 +114,7 @@ iterator mitems*(a: var cstring): var char {.inline.} = let n = len(a) while i < n: yield a[i] - inc(i) + unCheckedInc(i) when defined(js): impl() else: when nimvm: impl() @@ -117,7 +122,7 @@ iterator mitems*(a: var cstring): var char {.inline.} = var i = 0 while a[i] != '\0': yield a[i] - inc(i) + unCheckedInc(i) iterator items*[T: enum and Ordinal](E: typedesc[T]): T = ## Iterates over the values of `E`. @@ -140,7 +145,7 @@ iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} = var i = 0 while i < len(a): yield (i, a[i]) - inc(i) + unCheckedInc(i) iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. @@ -148,7 +153,7 @@ iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.} var i = 0 while i < len(a): yield (i, a[i]) - inc(i) + unCheckedInc(i) iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. @@ -157,7 +162,7 @@ iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} = while true: yield (i, a[i]) if i >= high(IX): break - inc(i) + unCheckedInc(i) iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. @@ -167,7 +172,7 @@ iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inlin while true: yield (i, a[i]) if i >= high(IX): break - inc(i) + unCheckedInc(i) iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. @@ -175,7 +180,7 @@ iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} = let L = len(a) while i < L: yield (i, a[i]) - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the seq changed while iterating over it") iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} = @@ -185,7 +190,7 @@ iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} = let L = len(a) while i < L: yield (i, a[i]) - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the seq changed while iterating over it") iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} = @@ -194,7 +199,7 @@ iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} = let L = len(a) while i < L: yield (i, a[i]) - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the string changed while iterating over it") iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} = @@ -204,7 +209,7 @@ iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} = let L = len(a) while i < L: yield (i, a[i]) - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the string changed while iterating over it") iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} = @@ -214,12 +219,12 @@ iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} = var L = len(a) while i < L: yield (i, a[i]) - inc(i) + unCheckedInc(i) else: var i = 0 while a[i] != '\0': yield (i, a[i]) - inc(i) + unCheckedInc(i) iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. @@ -229,12 +234,12 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = var L = len(a) while i < L: yield (i, a[i]) - inc(i) + unCheckedInc(i) else: var i = 0 while a[i] != '\0': yield (i, a[i]) - inc(i) + unCheckedInc(i) iterator items*[T](a: seq[T]): lent2 T {.inline.} = ## Iterates over each item of `a`. @@ -242,7 +247,7 @@ iterator items*[T](a: seq[T]): lent2 T {.inline.} = let L = len(a) while i < L: yield a[i] - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the seq changed while iterating over it") iterator mitems*[T](a: var seq[T]): var T {.inline.} = @@ -251,7 +256,7 @@ iterator mitems*[T](a: var seq[T]): var T {.inline.} = let L = len(a) while i < L: yield a[i] - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the seq changed while iterating over it") iterator items*(a: string): char {.inline.} = @@ -260,7 +265,7 @@ iterator items*(a: string): char {.inline.} = let L = len(a) while i < L: yield a[i] - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the string changed while iterating over it") iterator mitems*(a: var string): var char {.inline.} = @@ -269,7 +274,7 @@ iterator mitems*(a: var string): var char {.inline.} = let L = len(a) while i < L: yield a[i] - inc(i) + unCheckedInc(i) assert(len(a) == L, "the length of the string changed while iterating over it") diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index dfb0f0a3867a..b24e8e5a9d7b 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -91,7 +91,9 @@ try: `=copy`(lan_ip, splitted[1]) echo [lan_ip] echo [splitted[1]] + {.push, overflowChecks: false.} inc(i, 1) + {.pop.} finally: `=destroy`(splitted) finally: @@ -113,7 +115,9 @@ block :tmp: addInterfaceDecl(c): :tmpD = `=dup`(sym) :tmpD + {.push, overflowChecks: false.} inc(i, 1) + {.pop.} `=destroy`(shadowScope) -- end of expandArc ------------------------ --expandArc: check From ed30692d29745701c1219cb618ffcd0e50906fe3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 21 Sep 2023 06:35:48 +0800 Subject: [PATCH 293/347] =?UTF-8?q?fixes=20#22687;=20js=20backend=20-=20st?= =?UTF-8?q?d/bitops/bitsliced=20throws=20compile=20error=20=E2=80=A6=20(#2?= =?UTF-8?q?2722)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …in typeMasked fixes #22687 --- lib/pure/bitops.nim | 2 +- tests/stdlib/t21564.nim | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index d19c3d248beb..005c7fa8fa78 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -65,7 +65,7 @@ type BitsRange*[T] = range[0..sizeof(T)*8-1] template typeMasked[T: SomeInteger](x: T): T = when defined(js): - x and ((0xffffffff_ffffffff'u shr (64 - sizeof(T) * 8))) + T(x and ((0xffffffff_ffffffff'u shr (64 - sizeof(T) * 8)))) else: x diff --git a/tests/stdlib/t21564.nim b/tests/stdlib/t21564.nim index cb06155cf2d6..0a5777d12c7d 100644 --- a/tests/stdlib/t21564.nim +++ b/tests/stdlib/t21564.nim @@ -22,6 +22,10 @@ proc main() = # test `bitops.toMask` patch via bitops.masked doAssert(0x12FFFF34.masked(8..23) == 0x00FFFF00) + block: # bug #22687 + var a: uint8 = 0b1111_1111 + doAssert a.bitsliced(4..7).int == 15 + main() static: From b2896170130f1f998f7a1d51288fe66cbe345903 Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Thu, 21 Sep 2023 04:05:23 -0300 Subject: [PATCH 294/347] Documentation only (#22735) - Add Atomic ARC to Documentation. Documentation only, tiny diff. --- doc/mm.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/doc/mm.md b/doc/mm.md index 431517b176ba..cdc612e5ac8c 100644 --- a/doc/mm.md +++ b/doc/mm.md @@ -22,7 +22,7 @@ Multi-paradigm Memory Management Strategies Nim offers multiple different memory management strategies. To choose the memory management strategy use the `--mm:` switch. -**The recommended switch for newly written Nim code is `--mm:orc`.** + .. hint:: **The recommended switch for newly written Nim code is `--mm:orc`.** ARC/ORC @@ -73,17 +73,18 @@ Other MM modes Here is a comparison of the different memory management modes: -================== ======== ================= ============== =================== -Memory Management Heap Reference Cycles Stop-The-World Command line switch -================== ======== ================= ============== =================== -ORC Shared Cycle Collector No `--mm:orc` -ARC Shared Leak No `--mm:arc` -RefC Local Cycle Collector No `--mm:refc` -Mark & Sweep Local Cycle Collector No `--mm:markAndSweep` -Boehm Shared Cycle Collector Yes `--mm:boehm` -Go Shared Cycle Collector Yes `--mm:go` -None Manual Manual Manual `--mm:none` -================== ======== ================= ============== =================== +================== ======== ================= ============== ====== =================== +Memory Management Heap Reference Cycles Stop-The-World Atomic Command line switch +================== ======== ================= ============== ====== =================== +ORC Shared Cycle Collector No No `--mm:orc` +ARC Shared Leak No No `--mm:arc` +Atomic ARC Shared Leak No Yes `--mm:atomicArc` +RefC Local Cycle Collector No No `--mm:refc` +Mark & Sweep Local Cycle Collector No No `--mm:markAndSweep` +Boehm Shared Cycle Collector Yes No `--mm:boehm` +Go Shared Cycle Collector Yes No `--mm:go` +None Manual Manual Manual Manual `--mm:none` +================== ======== ================= ============== ====== =================== .. default-role:: code .. include:: rstcommon.rst From c75cbdde7034f10979b61687733f253261ecffec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Thu, 21 Sep 2023 12:56:00 +0100 Subject: [PATCH 295/347] moves `addUnique` to `std/sequtils` (#22734) Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/importer.nim | 6 +----- lib/pure/collections/sequtils.nim | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/compiler/importer.nim b/compiler/importer.nim index f5eb5329d919..dcdd0bb49e43 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -14,6 +14,7 @@ import semdata, modulepaths, sigmatch, lineinfos, sets, modulegraphs, wordrecg, tables from strutils import `%` +from sequtils import addUnique when defined(nimPreviewSlimSystem): import std/assertions @@ -228,11 +229,6 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im else: for i in 0..n.safeLen-1: importForwarded(c, n[i], exceptSet, fromMod, importSet) - -proc addUnique[T](x: var seq[T], y: sink T) {.noSideEffect.} = - for i in 0..high(x): - if x[i] == y: return - x.add y proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = result = realModule diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e2adba910ebe..eb9ff32c6923 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -140,6 +140,34 @@ func concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) +func addUnique*[T](s: var seq[T], x: sink T) = + ## Adds `x` to the container `s` if it is not already present. + ## Uses `==` to check if the item is already present. + runnableExamples: + var a = @[1, 2, 3] + a.addUnique(4) + a.addUnique(4) + assert a == @[1, 2, 3, 4] + + for i in 0..high(s): + if s[i] == x: return + when declared(ensureMove): + s.add ensureMove(x) + else: + s.add x + +func addUnique*[T](s: var seq[T], xs: sink seq[T]) = + ## Adds any items from `xs` to the container `s` that are not already present. + ## Uses `==` to check if the item is already present. + runnableExamples: + var a = @[1, 2, 3] + a.addUnique(@[3, 4]) + a.addUnique(@[4, 5]) + assert a == @[1, 2, 3, 4, 5] + + for i in 0..high(xs): + addUnique(s, move(xs[i])) + func count*[T](s: openArray[T], x: T): int = ## Returns the number of occurrences of the item `x` in the container `s`. ## From a1b6fa9420db09f2660ad5919d2b96673f324f8f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 22 Sep 2023 01:47:29 +0800 Subject: [PATCH 296/347] fixes #22246; generate __builtin_unreachable hints for case defaults (#22737) fixes #22246 resurrects #22350 --- compiler/ccgstmts.nim | 7 +++++-- compiler/extccomp.nim | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index de358e24260b..c212ca0cbf32 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -966,8 +966,11 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = hasDefault = true exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n", []) - if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault: - lineF(p, cpsStmts, "default: __assume(0);$n", []) + if not hasDefault: + if hasBuiltinUnreachable in CC[p.config.cCompiler].props: + lineF(p, cpsStmts, "default: __builtin_unreachable();$n", []) + elif hasAssume in CC[p.config.cCompiler].props: + lineF(p, cpsStmts, "default: __assume(0);$n", []) lineF(p, cpsStmts, "}$n", []) if lend != "": fixLabel(p, lend) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index be0bd36db65d..75d462b06d47 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -33,6 +33,7 @@ type hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax hasDeclspec, # CC has __declspec(X) hasAttribute, # CC has __attribute__((X)) + hasBuiltinUnreachable # CC has __builtin_unreachable TInfoCCProps* = set[TInfoCCProp] TInfoCC* = tuple[ name: string, # the short name of the compiler @@ -95,7 +96,7 @@ compiler gcc: produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasAttribute}) + hasAttribute, hasBuiltinUnreachable}) # GNU C and C++ Compiler compiler nintendoSwitchGCC: @@ -122,7 +123,7 @@ compiler nintendoSwitchGCC: produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++17 -funsigned-char", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, - hasAttribute}) + hasAttribute, hasBuiltinUnreachable}) # LLVM Frontend for GCC/G++ compiler llvmGcc: From c0838826c0071e9bf9a456a82b237b63f21f2b12 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 22 Sep 2023 11:38:30 +0800 Subject: [PATCH 297/347] fixes #22519; DocGen does not work for std/times on JS backend (#22738) fixes #22519 --- lib/pure/times.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 9c32c7b2113f..05a123055fd5 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -2787,7 +2787,9 @@ proc epochTime*(): float {.tags: [TimeEffect].} = ## ## .. warning:: Unsuitable for benchmarking (but still better than `now`), ## use `monotimes.getMonoTime` or `cpuTime` instead, depending on the use case. - when defined(macosx): + when defined(js): + result = newDate().getTime() / 1000 + elif defined(macosx): var a {.noinit.}: Timeval gettimeofday(a) result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat( @@ -2804,8 +2806,6 @@ proc epochTime*(): float {.tags: [TimeEffect].} = var secs = i64 div rateDiff var subsecs = i64 mod rateDiff result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001 - elif defined(js): - result = newDate().getTime() / 1000 else: {.error: "unknown OS".} From b10a809274494d46db2b6bf6930c8c50a2751c9b Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Sat, 23 Sep 2023 10:39:11 +0100 Subject: [PATCH 298/347] Make `newStringUninit` available on the js backend [backport] (#22743) --- lib/system.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 8cd526bd02af..97d9d72ad660 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1643,6 +1643,9 @@ when not defined(js): else: var s = cast[PGenericSeq](result) s.len = len +else: + proc newStringUninit*(len: Natural): string {. + magic: "NewString", importc: "mnewString", noSideEffect.} {.pop.} From eadd0d72cffd0d8470f42b873abda251e2e5eded Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Sat, 23 Sep 2023 15:10:17 +0100 Subject: [PATCH 299/347] Initialize `newString` in js [backport:1.6] (#22745) ```nim echo newString(8) ``` results in: ``` D:\User\test.js:25 var code_33556944 = c_33556931.toString(16); ^ TypeError: Cannot read properties of undefined (reading 'toString') at toJSStr (D:\User\test.js:25:50) at rawEcho (D:\User\test.js:70:16) at Object.<anonymous> (D:\User\test.js:101:1) at Module._compile (node:internal/modules/cjs/loader:1095:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1147:10) at Module.load (node:internal/modules/cjs/loader:975:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:17:47 Node.js v17.0.1 Error: execution of an external program failed: '"C:\Program Files\nodejs\node.exe" --unhandled-rejections=strict D:\User\test.js' ``` --- lib/system/jssys.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 3bf506e1e861..1c8ea9d88867 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -278,7 +278,9 @@ proc toJSStr(s: string): cstring {.compilerproc.} = proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = asm """ - return new Array(`len`); + var result = new Array(`len`); + for (var i = 0; i < `len`; i++) {result[i] = 0;} + return result; """ proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = From a6c281bd1d6e47fb8d137008e6ba944b8b2eb9a3 Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Sat, 23 Sep 2023 16:08:24 +0100 Subject: [PATCH 300/347] Fix `newStringUninit` not setting the '\0' terminator [backport] (#22746) Causes problems when working with `cstring`s. --- lib/system.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 97d9d72ad660..1ded4090b491 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1639,10 +1639,14 @@ when not defined(js): ## the same effect can be achieved with the `&` operator or with `add`. result = newStringOfCap(len) when defined(nimSeqsV2): - cast[ptr int](addr result)[] = len + let s = cast[ptr NimStringV2](addr result) + if len > 0: + s.len = len + s.p.data[len] = '\0' else: - var s = cast[PGenericSeq](result) + let s = cast[NimString](result) s.len = len + s.data[len] = '\0' else: proc newStringUninit*(len: Natural): string {. magic: "NewString", importc: "mnewString", noSideEffect.} From 1b0447c208a8ec03c1abf5c511b3949a882a8996 Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sun, 24 Sep 2023 20:47:56 +0200 Subject: [PATCH 301/347] Add magic toOpenArrayChar (#22751) Should help with stuff like the checksums package which only takes `openArray[char]` Co-authored-by: SirOlaf <> --- lib/system.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 1ded4090b491..64412b9b37bc 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2752,6 +2752,9 @@ proc toOpenArrayByte*(x: openArray[char]; first, last: int): openArray[byte] {. proc toOpenArrayByte*(x: seq[char]; first, last: int): openArray[byte] {. magic: "Slice".} +proc toOpenArrayChar*(x: openArray[byte]; first, last: int): openArray[char] {. + magic: "Slice".} + when defined(genode): var componentConstructHook*: proc (env: GenodeEnv) {.nimcall.} ## Hook into the Genode component bootstrap process. From 584010196856e4c357996696e932ba0ffbc61f84 Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Mon, 25 Sep 2023 00:21:09 -0300 Subject: [PATCH 302/347] Update Bisect (#22750) - Support multiple OS Bisects (Linux, Windows, MacOS). - Install Valgrind only if needed to speed up non-Valgrind builds. - Valgrind+MacOS bisect support. - Show IR of OK repro code samples. - YAML only, tiny diff. #### New features - Bisect bugs that only reproduce on Windows and OSX. #### See also - https://github.com/juancarlospaco/nimrun-action/pull/10 --- .github/workflows/bisects.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml index 066ebc80b3dd..e73736d2996a 100644 --- a/.github/workflows/bisects.yml +++ b/.github/workflows/bisects.yml @@ -5,21 +5,22 @@ on: types: created jobs: - test: - runs-on: ubuntu-latest + bisects: + if: | + github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE' + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, windows-latest, macos-latest] + name: ${{ matrix.platform }}-bisects + runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - # nimrun-action requires Nim installed. - - uses: jiro4989/setup-nim-action@v1 - with: - nim-version: 'devel' + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: 'devel' - - name: Install Dependencies - run: | - sudo apt-get -yq update - sudo apt-get install --no-install-recommends -yq valgrind - - - uses: juancarlospaco/nimrun-action@nim - with: - github-token: ${{ secrets.GITHUB_TOKEN }} + - uses: juancarlospaco/nimrun-action@nim + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From f0bf94e5311e851cba4370c2310c0be66e535a27 Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili <amjadhedhili@outlook.com> Date: Mon, 25 Sep 2023 06:19:09 +0100 Subject: [PATCH 303/347] Make `newStringUninit` available in the VM [backport] (#22748) It's equivalent to `newString`. --- lib/system.nim | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 64412b9b37bc..8e16d17d4e51 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1637,16 +1637,19 @@ when not defined(js): ## ## This procedure exists only for optimization purposes; ## the same effect can be achieved with the `&` operator or with `add`. - result = newStringOfCap(len) - when defined(nimSeqsV2): - let s = cast[ptr NimStringV2](addr result) - if len > 0: - s.len = len - s.p.data[len] = '\0' + when nimvm: + result = newString(len) else: - let s = cast[NimString](result) - s.len = len - s.data[len] = '\0' + result = newStringOfCap(len) + when defined(nimSeqsV2): + let s = cast[ptr NimStringV2](addr result) + if len > 0: + s.len = len + s.p.data[len] = '\0' + else: + let s = cast[NimString](result) + s.len = len + s.data[len] = '\0' else: proc newStringUninit*(len: Natural): string {. magic: "NewString", importc: "mnewString", noSideEffect.} From 3979e83fcbf5e7bda9a1bad55fa9d594f36158de Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 25 Sep 2023 23:51:30 +0800 Subject: [PATCH 304/347] fixes #22706; turn "unknown hint" into a hint (#22755) fixes #22706 --- compiler/commands.nim | 6 +++++- compiler/lineinfos.nim | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index ac5366f10fc9..8c1da3cd3263 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -205,7 +205,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, # unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit let x = findStr(noteMin, noteMax, id, errUnknown) if x != errUnknown: notes = {TNoteKind(x)} - else: localError(conf, info, "unknown $#: $#" % [name, id]) + else: + if isSomeHint: + message(conf, info, hintUnknownHint, id) + else: + localError(conf, info, "unknown $#: $#" % [name, id]) case id.normalize of "all": # other note groups would be easy to support via additional cases notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax} diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 7cc9c68f5dd3..544c5295c7bc 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -107,6 +107,7 @@ type hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext", hintMsgOrigin = "MsgOrigin", # since 1.3.5 hintDeclaredLoc = "DeclaredLoc", # since 1.5.1 + hintUnknownHint = "UnknownHint" const MsgKindToStr*: array[TMsgKind, string] = [ @@ -229,6 +230,7 @@ const hintExtendedContext: "$1", hintMsgOrigin: "$1", hintDeclaredLoc: "$1", + hintUnknownHint: "unknown hint: $1" ] const From 46544f234df3c473e6cd798eca4754c84be96abc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 26 Sep 2023 17:39:59 +0800 Subject: [PATCH 305/347] fixes stint CI (#22756) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 873da0dbd08a..60019dd8d89f 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -150,7 +150,7 @@ pkg "smtp", "nimble compileExample" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "ssostrings" pkg "stew" -pkg "stint", "nim r stint.nim" +pkg "stint", "nim c stint.nim" pkg "strslice" pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim" pkg "supersnappy" From 435f9320889f21269a26a17f724b73c4a46f9f23 Mon Sep 17 00:00:00 2001 From: Jake Leahy <jake@leahy.dev> Date: Tue, 26 Sep 2023 19:40:18 +1000 Subject: [PATCH 306/347] Add test case for #15351 (#22754) Closes #15351 Stumbled across the issue and found it now works --- tests/misc/t15351.nim | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/misc/t15351.nim diff --git a/tests/misc/t15351.nim b/tests/misc/t15351.nim new file mode 100644 index 000000000000..c31e604a2a9e --- /dev/null +++ b/tests/misc/t15351.nim @@ -0,0 +1,5 @@ +discard """ + action: "compile" +""" +var + ## TODO: broken From a7a0cfe8eb5942d799b4b7e2fdf26bcb9ff8ffab Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 26 Sep 2023 20:05:18 +0800 Subject: [PATCH 307/347] fixes #10542; suppresses varargs conversion warnings (#22757) fixes #10542 revives and close #20169 --- compiler/semexprs.nim | 3 ++- compiler/sigmatch.nim | 2 +- tests/errmsgs/t10542.nim | 24 ++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/errmsgs/t10542.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 590d2610be92..633a0cc26758 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -390,7 +390,8 @@ proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple: op = fitNode(c, targetType, op, result.info) of convNotNeedeed: - message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) + if efNoSem2Check notin flags: + message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: result = fitNode(c, result.typ, result[1], result.info) if result == nil: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 1cf29dcfd465..ee93321c8b8c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2060,7 +2060,7 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, if result != nil: if result.typ == nil: return nil # bug #13378, ensure we produce a real generic instantiation: - result = c.semExpr(c, call) + result = c.semExpr(c, call, {efNoSem2Check}) # resulting type must be consistent with the other arguments: var r = typeRel(m, f[0], result.typ) if r < isGeneric: return nil diff --git a/tests/errmsgs/t10542.nim b/tests/errmsgs/t10542.nim new file mode 100644 index 000000000000..b57dbf18ba8d --- /dev/null +++ b/tests/errmsgs/t10542.nim @@ -0,0 +1,24 @@ +discard """ + matrix: "--hintaserror:ConvFromXtoItselfNotNeeded" +""" + +# bug #10542 + +proc f(args: varargs[string, string], length: int) = + doAssert args.len == length + +# main use case that requires type conversion (no warning here) +f("a", "b", 2) +f("a", 1) + + +proc m(args: varargs[cstring, cstring]) = + doAssert args.len == 2 + +# main use case that requires type conversion (no warning here) +m("a", "b") + +# if an argument already is cstring there's a warning +let x: cstring = "x" +m("a", x) +m(x, "a") \ No newline at end of file From 21218d743fad5c0c7f0ef6a6a189178e4b1effed Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Wed, 27 Sep 2023 00:49:17 -0300 Subject: [PATCH 308/347] Documentation only (#22761) - Mention Bisect bot in Bisect documentation. --- doc/intern.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/intern.md b/doc/intern.md index e1861ea7d03d..6b16bc71faef 100644 --- a/doc/intern.md +++ b/doc/intern.md @@ -107,6 +107,10 @@ You can also bisect using custom options to build the compiler, for example if you don't need a debug version of the compiler (which runs slower), you can replace `./koch temp`:cmd: by explicit compilation command, see [Bootstrapping the compiler]. +See also: + +- Crossplatform C/Cpp/Valgrind/JS Bisect in GitHub: https://github.com/juancarlospaco/nimrun-action#examples + Building an instrumented compiler --------------------------------- From 02ba28eda5ea9b7cb6545f08e0620875f0100bf3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:35:41 +0800 Subject: [PATCH 309/347] iNim switch to the official URL (#22762) ref https://github.com/inim-repl/INim/pull/139 --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 60019dd8d89f..462bf8a30d28 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -80,7 +80,7 @@ pkg "gnuplot", "nim c gnuplot.nim" pkg "hts", "nim c -o:htss src/hts.nim" pkg "httpauth" pkg "illwill", "nimble examples" -pkg "inim", url = "https://github.com/nim-lang/INim" +pkg "inim" pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" pkg "jstin" From a9e6660a74322f0385f22c89e60c46e3177c2513 Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Wed, 27 Sep 2023 12:59:26 -0300 Subject: [PATCH 310/347] Documentation only (#22760) - Documentation only. - Sometimes newbies try to use Valgrind with RefC etc. --- doc/mm.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/mm.md b/doc/mm.md index cdc612e5ac8c..5e0d2f3b943f 100644 --- a/doc/mm.md +++ b/doc/mm.md @@ -73,18 +73,18 @@ Other MM modes Here is a comparison of the different memory management modes: -================== ======== ================= ============== ====== =================== -Memory Management Heap Reference Cycles Stop-The-World Atomic Command line switch -================== ======== ================= ============== ====== =================== -ORC Shared Cycle Collector No No `--mm:orc` -ARC Shared Leak No No `--mm:arc` -Atomic ARC Shared Leak No Yes `--mm:atomicArc` -RefC Local Cycle Collector No No `--mm:refc` -Mark & Sweep Local Cycle Collector No No `--mm:markAndSweep` -Boehm Shared Cycle Collector Yes No `--mm:boehm` -Go Shared Cycle Collector Yes No `--mm:go` -None Manual Manual Manual Manual `--mm:none` -================== ======== ================= ============== ====== =================== +================== ======== ================= ============== ====== =================== =================== +Memory Management Heap Reference Cycles Stop-The-World Atomic Valgrind compatible Command line switch +================== ======== ================= ============== ====== =================== =================== +ORC Shared Cycle Collector No No Yes `--mm:orc` +ARC Shared Leak No No Yes `--mm:arc` +Atomic ARC Shared Leak No Yes Yes `--mm:atomicArc` +RefC Local Cycle Collector No No No `--mm:refc` +Mark & Sweep Local Cycle Collector No No No `--mm:markAndSweep` +Boehm Shared Cycle Collector Yes No No `--mm:boehm` +Go Shared Cycle Collector Yes No No `--mm:go` +None Manual Manual Manual Manual Manual `--mm:none` +================== ======== ================= ============== ====== =================== =================== .. default-role:: code .. include:: rstcommon.rst From f0865fa6960cbff301318f4ef8d603052519b24d Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Thu, 28 Sep 2023 02:37:09 -0300 Subject: [PATCH 311/347] Fix #21407 (#22759) - Fix #21407 --------- Co-authored-by: Amjad Ben Hedhili <amjadhedhili@outlook.com> --- changelog.md | 1 + lib/js/dom.nim | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 4ea436fb0a67..b661226be4c4 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,7 @@ - Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. - Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value. +- Added Viewport API for the JavaScript targets in the `dom` module. [//]: # "Deprecations:" diff --git a/lib/js/dom.nim b/lib/js/dom.nim index ceb0375b7c9d..ba9b4159db68 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1832,3 +1832,11 @@ since (1, 7): proc matches*(self: Node; cssSelector: cstring): bool {.importjs: "(#.$1(#) || false)".} ## https://developer.mozilla.org/en-US/docs/Web/API/Element/matches + + +since (2, 1): + type VisualViewport* {.importc.} = ref object of EventTarget + offsetLeft*, offsetTop*, pageLeft*, pageTop*, width*, height*, scale*: float + onResize*, onScroll*: proc (event: Event) {.closure.} + + func visualViewport*(self: Window): VisualViewport {.importjs: "#.$1", nodecl.} From 4bf0f846df3bdeeeeff288f1d28688ee4852bc71 Mon Sep 17 00:00:00 2001 From: Thiago <74574275+thisago@users.noreply.github.com> Date: Thu, 28 Sep 2023 09:30:04 +0000 Subject: [PATCH 312/347] Removed `localStorage.hasKey` binding (#22766) Doesn't exists anymore. Use `window.localStorage.getItem("key").isNil` instead ![Screenshot from 2023-09-28 07-22-41](https://github.com/nim-lang/Nim/assets/74574275/65d58921-58c7-4a81-9f3b-5faa3a79c4f2) --- lib/js/dom.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index ba9b4159db68..eb05308bb7bd 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1682,7 +1682,6 @@ proc `$`*(s: Selection): string = $(s.toString()) # Storage "methods" proc getItem*(s: Storage, key: cstring): cstring proc setItem*(s: Storage, key, value: cstring) -proc hasItem*(s: Storage, key: cstring): bool proc clear*(s: Storage) proc removeItem*(s: Storage, key: cstring) From 285cbcb6aa36147e9b9c024ceed78e46526dd1ea Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 29 Sep 2023 00:08:31 +0800 Subject: [PATCH 313/347] ref #19727; implement `setLenUninit` for seqsv2 (#22767) ref #19727 --- changelog.md | 2 ++ lib/system/seqs_v2.nim | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/changelog.md b/changelog.md index b661226be4c4..e0eb34470f4c 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,8 @@ [//]: # "Additions:" - Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. +- Added `setLenUninit` to system, which doesn't initalize +slots when enlarging a sequence. - Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value. - Added Viewport API for the JavaScript targets in the `dom` module. diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index c4e3e3e6b1cb..7ce8de05453e 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -195,5 +195,29 @@ func capacity*[T](self: seq[T]): int {.inline.} = let sek = cast[ptr NimSeqV2[T]](unsafeAddr self) result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0 +func setLenUninit*[T](s: var seq[T], newlen: Natural) {.nodestroy.} = + ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type. + ## New slots will not be initialized. + ## + ## If the current length is greater than the new length, + ## `s` will be truncated. + ## ```nim + ## var x = @[10, 20] + ## x.setLenUninit(5) + ## x[4] = 50 + ## assert x[4] == 50 + ## x.setLenUninit(1) + ## assert x == @[10] + ## ``` + {.noSideEffect.}: + if newlen < s.len: + shrink(s, newlen) + else: + let oldLen = s.len + if newlen <= oldLen: return + var xu = cast[ptr NimSeqV2[T]](addr s) + if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen: + xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T))) + xu.len = newlen {.pop.} # See https://github.com/nim-lang/Nim/issues/21401 From 4fffa0960f6c17e9e1e6a20665c97ac34ea678bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Thu, 28 Sep 2023 17:08:42 +0100 Subject: [PATCH 314/347] C++ Adds support for default arg using object construction syntax. Fixes a compiler crash (#22768) `Foo()` below makes the compiler crash. ```nim proc makeBoo(a:cint = 10, b:cstring = "hello", foo: Foo = Foo()): Boo {.importcpp, constructor.} ``` --- compiler/ccgstmts.nim | 7 ++++++- tests/cpp/tinitializers.nim | 31 +++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index c212ca0cbf32..c8f68c124eba 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -296,7 +296,12 @@ proc genCppParamsForCtor(p: BProc; call: PNode): string = assert(typ.kind == tyProc) for i in 1..<call.len: assert(typ.len == typ.n.len) - genOtherArg(p, call, i, typ, result, argsCounter) + #if it's a type we can just generate here another initializer as we are in an initializer context + if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType: + if argsCounter > 0: result.add "," + result.add genCppInitializer(p.module, p, call[i][0].sym.typ) + else: + genOtherArg(p, call, i, typ, result, argsCounter) proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) = let params = genCppParamsForCtor(p, call) diff --git a/tests/cpp/tinitializers.nim b/tests/cpp/tinitializers.nim index 868cf825ca86..0199fb96bcee 100644 --- a/tests/cpp/tinitializers.nim +++ b/tests/cpp/tinitializers.nim @@ -1,5 +1,5 @@ discard """ - targets: "cpp" + cmd: "nim cpp $file" """ {.emit:"""/*TYPESECTION*/ @@ -30,4 +30,31 @@ proc main = discard returnCppStruct() #generates result = { 10 } discard initChildStruct() #generates ChildStruct temp ({}) bypassed with makeChildStruct (proc (s:CppStruct) = discard)(CppStruct()) #CppStruct temp ({10}) -main() \ No newline at end of file +main() + + +#Should handle ObjectCalls +{.emit:"""/*TYPESECTION*/ +struct Foo { +}; +struct Boo { + Boo(int x, char* y, Foo f): x(x), y(y), foo(f){} + int x; + char* y; + Foo foo; +}; +""".} +type + Foo {.importcpp, inheritable, bycopy.} = object + Boo {.importcpp, inheritable.} = object + x: int32 + y: cstring + foo: Foo + +proc makeBoo(a:cint = 10, b:cstring = "hello", foo: Foo = Foo()): Boo {.importcpp, constructor.} + +proc main2() = + let cppStruct = makeBoo() + (proc (s:Boo) = discard)(Boo()) + +main2() \ No newline at end of file From 8761599aade64f5953ef7d3c4cea005a0c459355 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 29 Sep 2023 00:09:58 +0800 Subject: [PATCH 315/347] fixes #22763; nimcache in nim.cfg uses the relative path to the config file (#22764) fixes #22763 --- changelog.md | 1 + compiler/commands.nim | 9 ++++++++- compiler/nimconf.nim | 5 +++-- compiler/options.nim | 3 +++ compiler/scriptconfig.nim | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index e0eb34470f4c..795386144566 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,7 @@ slots when enlarging a sequence. ## Compiler changes +- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory. ## Tool changes diff --git a/compiler/commands.nim b/compiler/commands.nim index 8c1da3cd3263..f36d82306f3e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -608,6 +608,13 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, defineSymbol(conf.symbols, "gcregions") else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) +proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): string = + if pass == passPP and not isAbsolute(arg): + assert isAbsolute(conf.currentConfigDir), "something is wrong with currentConfigDir" + result = conf.currentConfigDir / arg + else: + result = arg + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var key = "" @@ -653,7 +660,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; # refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set # in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")` if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/') - conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true) + conf.nimcacheDir = processPath(conf, pathRelativeToConfig(arg, pass, conf), info, notRelativeToProj=true) of "out", "o": expectArg(conf, switch, arg, pass, info) let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index f7bae4b36838..78215d281a66 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -162,7 +162,7 @@ proc checkSymbol(L: Lexer, tok: Token) = lexMessage(L, errGenerated, "expected identifier, but got: " & $tok) proc parseAssignment(L: var Lexer, tok: var Token; - config: ConfigRef; condStack: var seq[bool]) = + config: ConfigRef; filename: AbsoluteFile; condStack: var seq[bool]) = if tok.ident != nil: if tok.ident.s == "-" or tok.ident.s == "--": confTok(L, tok, config, condStack) # skip unnecessary prefix @@ -205,6 +205,7 @@ proc parseAssignment(L: var Lexer, tok: var Token; checkSymbol(L, tok) val.add($tok) confTok(L, tok, config, condStack) + config.currentConfigDir = parentDir(filename.string) if percent: processSwitch(s, strtabs.`%`(val, config.configVars, {useEnvironment, useEmpty}), passPP, info, config) @@ -224,7 +225,7 @@ proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; tok.tokType = tkEof # to avoid a pointless warning var condStack: seq[bool] = @[] confTok(L, tok, config, condStack) # read in the first token - while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack) + while tok.tokType != tkEof: parseAssignment(L, tok, config, filename, condStack) if condStack.len > 0: lexMessage(L, errGenerated, "expected @end") closeLexer(L) return true diff --git a/compiler/options.nim b/compiler/options.nim index c18ca92dc189..a2f50b12bd28 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -421,6 +421,8 @@ type expandNodeResult*: string expandPosition*: TLineInfo + currentConfigDir*: string # used for passPP only; absolute dir + proc parseNimVersion*(a: string): NimVer = # could be moved somewhere reusable @@ -585,6 +587,7 @@ proc newConfigRef*(): ConfigRef = maxLoopIterationsVM: 10_000_000, vmProfileData: newProfileData(), spellSuggestMax: spellSuggestSecretSauce, + currentConfigDir: "" ) initConfigRefCommon(result) setTargetFromSystem(result.target) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 1a686fd20f45..c89767296b26 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -160,6 +160,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf getCommand: setResult(a, conf.command) cbconf switch: + conf.currentConfigDir = vthisDir processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf) cbconf hintImpl: processSpecificNote(a.getString 0, wHint, passPP, module.info, From a8d55fdec7e6e534546f9d6c116d9a76393c534b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 29 Sep 2023 15:38:51 +0800 Subject: [PATCH 316/347] deprecates `newSeqUninitialized` replaced by `newSeqUninit` (#22739) ref #19727 closes #22586 https://github.com/nim-lang/Nim/issues/22554 needs it to move on. `newSeqUnsafe` can be introduced later. --- changelog.md | 1 + lib/system.nim | 31 +++++++++++++++++++++++++++++-- lib/system/seqs_v2.nim | 2 -- testament/important_packages.nim | 4 ++-- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 795386144566..74020e8fa97e 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,7 @@ slots when enlarging a sequence. [//]: # "Deprecations:" +- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`. [//]: # "Removals:" diff --git a/lib/system.nim b/lib/system.nim index 8e16d17d4e51..b9dd7f143541 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -627,7 +627,7 @@ proc newSeq*[T](len = 0.Natural): seq[T] = ## ## See also: ## * `newSeqOfCap <#newSeqOfCap,Natural>`_ - ## * `newSeqUninitialized <#newSeqUninitialized,Natural>`_ + ## * `newSeqUninit <#newSeqUninit,Natural>`_ newSeq(result, len) proc newSeqOfCap*[T](cap: Natural): seq[T] {. @@ -1606,12 +1606,22 @@ when not defined(js) and defined(nimV2): flags: int PNimTypeV2 = ptr TNimTypeV2 +proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} + when notJSnotNims and defined(nimSeqsV2): include "system/strs_v2" include "system/seqs_v2" when not defined(js): - proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] = + template newSeqImpl(T, len) = + result = newSeqOfCap[T](len) + when defined(nimSeqsV2): + cast[ptr int](addr result)[] = len + else: + var s = cast[PGenericSeq](result) + s.len = len + + proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] {.deprecated: "Use `newSeqUninit` instead".} = ## Creates a new sequence of type `seq[T]` with length `len`. ## ## Only available for numbers types. Note that the sequence will be @@ -1630,6 +1640,23 @@ when not defined(js): var s = cast[PGenericSeq](result) s.len = len + proc newSeqUninit*[T](len: Natural): seq[T] = + ## Creates a new sequence of type `seq[T]` with length `len`. + ## + ## Only available for types, which don't contain + ## managed memory or have destructors. + ## Note that the sequence will be uninitialized. + ## After the creation of the sequence you should assign + ## entries to the sequence instead of adding them. + runnableExamples: + var x = newSeqUninit[int](3) + assert len(x) == 3 + x[0] = 10 + when supportsCopyMem(T): + newSeqImpl(T, len) + else: + {.error: "The type T cannot contain managed memory or have destructors".} + proc newStringUninit*(len: Natural): string = ## Returns a new string of length `len` but with uninitialized ## content. One needs to fill the string character after character diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 7ce8de05453e..ee8f2d67eb5c 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -16,8 +16,6 @@ {.push warning[StrictNotNil]: off.} # See https://github.com/nim-lang/Nim/issues/21401 -proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} - ## Default seq implementation used by Nim's core. type NimSeqPayloadBase = object diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 462bf8a30d28..f769c4ebda36 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -35,7 +35,7 @@ proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailu pkg "alea" pkg "argparse" -pkg "arraymancer", "nim c tests/tests_cpu.nim" +pkg "arraymancer", "nim c tests/tests_cpu.nim", url = "https://github.com/nim-lang/Arraymancer" pkg "ast_pattern_matching", "nim c -r tests/test1.nim" pkg "asyncftpclient", "nimble compileExample" pkg "asyncthreadpool", "nimble test --mm:refc" @@ -96,7 +96,7 @@ pkg "measuremancer", "nimble testDeps; nimble -y test" pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" -pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim" +pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim", url = "https://github.com/nim-lang/neo" pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true pkg "netty" pkg "nico", allowFailure = true From 0c179db6579ee5ae853603bf8d8efe23785cb7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Sat, 30 Sep 2023 05:27:02 +0100 Subject: [PATCH 317/347] case macro now can be used inside generic. Fixes #20435 (#22752) fixes #20435 --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> Co-authored-by: Jake Leahy <jake@leahy.dev> Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/semgnrc.nim | 16 +++++++++++++++- tests/macros/t20435.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/macros/t20435.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index aa05f8d85d70..eebf11c0a0a7 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -188,6 +188,18 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = styleCheckDef(c, n.info, s, kind) onDef(n.info, s) +proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) = + case n.kind + of nkIdent: + if inCall: + addTempDecl(c, n, kind) + of nkCallKinds: + for s in n: + addTempDeclToIdents(c, s, kind, true) + else: + for s in n: + addTempDeclToIdents(c, s, kind, inCall) + proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n @@ -360,7 +372,9 @@ proc semGenericStmt(c: PContext, n: PNode, var a = n[i] checkMinSonsLen(a, 1, c.config) for j in 0..<a.len-1: - a[j] = semGenericStmt(c, a[j], flags, ctx) + a[j] = semGenericStmt(c, a[j], flags+{withinMixin}, ctx) + addTempDeclToIdents(c, a[j], skVar, false) + a[^1] = semGenericStmtScope(c, a[^1], flags, ctx) closeScope(c) of nkForStmt, nkParForStmt: diff --git a/tests/macros/t20435.nim b/tests/macros/t20435.nim new file mode 100644 index 000000000000..824282198cfe --- /dev/null +++ b/tests/macros/t20435.nim @@ -0,0 +1,30 @@ + +#[ + A better test requires matching, so the use of @ working can be showcased + For example: + + proc regularCase[T]() = + case [(1, 3), (3, 4)]: + of [(1, @a), (_, @b)]: + echo a, b + else: discard +]# + +{.experimental: "caseStmtMacros".} + +import macros + +type Foo = object + +macro `case`(obj: Foo) = quote do: discard + +proc notGeneric() = + case Foo() + of a b c d: discard + +proc generic[T]() = + case Foo() + of a b c d: discard + +notGeneric() +generic[int]() From 5eeafbf55029816e5022d9d6aa9ed3b0acf2d3ba Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 30 Sep 2023 12:27:27 +0800 Subject: [PATCH 318/347] fixes #22696; func `strutils.join` for non-strings uses proc `$` which can have side effects (#22770) fixes #22696 partially revert https://github.com/nim-lang/Nim/pull/16281 `join` calls `$` interally, which might introduce a sideeffect call. --- lib/pure/strutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 7ab3d37c80a7..5e41705a724a 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1875,7 +1875,7 @@ func join*(a: openArray[string], sep: string = ""): string {.rtl, else: result = "" -func join*[T: not string](a: openArray[T], sep: string = ""): string = +proc join*[T: not string](a: openArray[T], sep: string = ""): string = ## Converts all elements in the container `a` to strings using `$`, ## and concatenates them with `sep`. runnableExamples: From a38e3dcb1f0c47b89f07b343a259c4b10a333ac9 Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Sat, 30 Sep 2023 01:31:28 -0300 Subject: [PATCH 319/347] copyFile with bufferSize instead of hardcoded value (#22769) - `copyFile` allows to specify `bufferSize` instead of hardcoded wrong value. Tiny diff. # Performance - 1200% Performance improvement. # Check it yourself Execute: ```bash for i in $(seq 0 10); do bs=$((1024*2**$i)) printf "%7s Kb\t" $bs timeout --foreground -sINT 2 dd bs=$bs if=/dev/zero of=/dev/null 2>&1 | sed -n 's/.* \([0-9.,]* [GM]B\/s\)/\1/p' done ``` (This script can be ported to PowerShell for Windows I guess, it works in Windows MinGW Bash anyways). # Stats - Hardcoded `8192` or `8000` Kb bufferSize gives `5` GB/s. - Setting `262144` Kb bufferSize gives `65` GB/s (script suggestion). --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- changelog.md | 1 + lib/std/private/osfiles.nim | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index 74020e8fa97e..1fb6377a2848 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ [//]: # "Changes:" +- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one. [//]: # "Additions:" diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index 4596adfb273f..b7957fa4f1ba 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -173,7 +173,7 @@ type const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore} -proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, +proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 16_384) {.rtl, extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} = ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist. @@ -202,6 +202,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, ## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless ## `-d:nimLegacyCopyFile` is used. ## + ## `copyFile` allows to specify `bufferSize` to improve I/O performance. ## See also: ## * `CopyFlag enum`_ ## * `copyDir proc`_ @@ -210,8 +211,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, ## * `removeFile proc`_ ## * `moveFile proc`_ - doAssert card(copyFlagSymlink * options) == 1, "There should be exactly " & - "one cfSymlink* in options" + doAssert card(copyFlagSymlink * options) == 1, "There should be exactly one cfSymlink* in options" let isSymlink = source.symlinkExists if isSymlink and (cfSymlinkIgnore in options or defined(windows)): return @@ -238,15 +238,14 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, if status2 != 0: raiseOSError(osLastError(), $(source, dest)) else: # generic version of copyFile which works for any platform: - const bufSize = 8000 # better for memory manager var d, s: File if not open(s, source):raiseOSError(osLastError(), source) if not open(d, dest, fmWrite): close(s) raiseOSError(osLastError(), dest) - var buf = alloc(bufSize) + var buf = alloc(bufferSize) while true: - var bytesread = readBuffer(s, buf, bufSize) + var bytesread = readBuffer(s, buf, bufferSize) if bytesread > 0: var byteswritten = writeBuffer(d, buf, bytesread) if bytesread != byteswritten: @@ -254,13 +253,13 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, close(s) close(d) raiseOSError(osLastError(), dest) - if bytesread != bufSize: break + if bytesread != bufferSize: break dealloc(buf) close(s) flushFile(d) close(d) -proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) +proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}; bufferSize = 16_384) {.noWeirdTarget, since: (1,3,7).} = ## Copies a file `source` into directory `dir`, which must exist. ## @@ -268,12 +267,14 @@ proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) ## if `source` is a symlink, copies the file symlink points to. `options` is ## ignored on Windows: symlinks are skipped. ## + ## `copyFileToDir` allows to specify `bufferSize` to improve I/O performance. + ## ## See also: ## * `CopyFlag enum`_ ## * `copyFile proc`_ if dir.len == 0: # treating "" as "." is error prone raise newException(ValueError, "dest is empty") - copyFile(source, dir / source.lastPathPart, options) + copyFile(source, dir / source.lastPathPart, options, bufferSize) proc copyFileWithPermissions*(source, dest: string, From 714630782371565e3982a3e70e288c7d1e2dd88b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 30 Sep 2023 12:32:27 +0800 Subject: [PATCH 320/347] fixes #22554; makes `newSeqWith` use `newSeqUninit` (#22771) fixes #22554 --- lib/pure/collections/sequtils.nim | 7 ++++++- lib/system.nim | 13 +++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index eb9ff32c6923..9c33b2eb6c76 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -83,6 +83,7 @@ runnableExamples: import std/private/since import macros +from typetraits import supportsCopyMem when defined(nimPreviewSlimSystem): import std/assertions @@ -1114,8 +1115,12 @@ template newSeqWith*(len: int, init: untyped): untyped = import std/random var seqRand = newSeqWith(20, rand(1.0)) assert seqRand[0] != seqRand[1] + type T = typeof(init) let newLen = len - var result = newSeq[typeof(init)](newLen) + when supportsCopyMem(T) and declared(newSeqUninit): + var result = newSeqUninit[T](newLen) + else: # TODO: use `newSeqUnsafe` when that's available + var result = newSeq[T](newLen) for i in 0 ..< newLen: result[i] = init move(result) # refs bug #7295 diff --git a/lib/system.nim b/lib/system.nim index b9dd7f143541..3e7d84b1dfb2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1615,11 +1615,12 @@ when notJSnotNims and defined(nimSeqsV2): when not defined(js): template newSeqImpl(T, len) = result = newSeqOfCap[T](len) - when defined(nimSeqsV2): - cast[ptr int](addr result)[] = len - else: - var s = cast[PGenericSeq](result) - s.len = len + {.cast(noSideEffect).}: + when defined(nimSeqsV2): + cast[ptr int](addr result)[] = len + else: + var s = cast[PGenericSeq](result) + s.len = len proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] {.deprecated: "Use `newSeqUninit` instead".} = ## Creates a new sequence of type `seq[T]` with length `len`. @@ -1640,7 +1641,7 @@ when not defined(js): var s = cast[PGenericSeq](result) s.len = len - proc newSeqUninit*[T](len: Natural): seq[T] = + func newSeqUninit*[T](len: Natural): seq[T] = ## Creates a new sequence of type `seq[T]` with length `len`. ## ## Only available for types, which don't contain From b2ca6bedae6420396682cbf06b3c628cfcf9baf1 Mon Sep 17 00:00:00 2001 From: Ryan McConnell <rammcconnell@gmail.com> Date: Sat, 30 Sep 2023 04:34:14 +0000 Subject: [PATCH 321/347] Make `typeRel` behave to spec (#22261) The goal of this PR is to make `typeRel` accurate to it's definition for generics: ``` # 3) When used with two type classes, it will check whether the types # matching the first type class (aOrig) are a strict subset of the types matching # the other (f). This allows us to compare the signatures of generic procs in # order to give preferrence to the most specific one: ``` I don't want this PR to break any code, and I want to preserve all of Nims current behaviors. I think that making this more accurate will help serve as ground work for the future. It may not be possible to not break anything but this is my attempt. So that it is understood, this code was part of another PR (#22143) but that problem statement only needed this change by extension. It's more organized to split two problems into two PRs and this issue, being non-breaking, should be a more immediate improvement. --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/sigmatch.nim | 75 ++++++++++++++++++++++++------ doc/manual_experimental.md | 1 + tests/overload/tor_isnt_better.nim | 25 +++++----- tests/stdlib/t21406.nim | 6 ++- 4 files changed, 78 insertions(+), 29 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ee93321c8b8c..3867a67b7047 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -453,6 +453,38 @@ proc handleFloatRange(f, a: PType): TTypeRelation = else: result = isIntConv else: result = isNone +proc getObjectTypeOrNil(f: PType): PType = + #[ + Returns a type that is f's effective typeclass. This is usually just one level deeper + in the hierarchy of generality for a type. `object`, `ref object`, `enum` and user defined + tyObjects are common return values. + ]# + if f == nil: return nil + case f.kind: + of tyGenericInvocation, tyCompositeTypeClass, tyAlias: + if f.len <= 0 or f[0] == nil: + result = nil + else: + result = getObjectTypeOrNil(f[0]) + of tyGenericBody, tyGenericInst: + result = getObjectTypeOrNil(f.lastSon) + of tyUserTypeClass: + if f.isResolvedUserTypeClass: + result = f.base # ?? idk if this is right + else: + result = f.lastSon + of tyStatic, tyOwned, tyVar, tyLent, tySink: + result = getObjectTypeOrNil(f.base) + of tyInferred: + # This is not true "After a candidate type is selected" + result = getObjectTypeOrNil(f.base) + of tyTyped, tyUntyped, tyFromExpr: + result = nil + of tyRange: + result = f.lastSon + else: + result = f + proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = if fGenericOrigin != nil and last.kind == tyGenericInst and last.len-1 == fGenericOrigin.len: @@ -467,7 +499,8 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int = var depth = 0 var last = a while t != nil and not sameObjectTypes(f, t): - assert t.kind == tyObject + if t.kind != tyObject: # avoid entering generic params etc + return -1 t = t[0] if t == nil: break last = t @@ -997,8 +1030,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # of the designated type class. # # 3) When used with two type classes, it will check whether the types - # matching the first type class are a strict subset of the types matching - # the other. This allows us to compare the signatures of generic procs in + # matching the first type class (aOrig) are a strict subset of the types matching + # the other (f). This allows us to compare the signatures of generic procs in # order to give preferrence to the most specific one: # # seq[seq[any]] is a strict subset of seq[any] and hence more specific. @@ -1154,7 +1187,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # being passed as parameters return isNone else: discard - case f.kind of tyEnum: if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual @@ -1245,6 +1277,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return inferStaticsInRange(c, fRange, a) elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) + elif result == isGeneric and concreteType(c, aa, ff) == nil: + return isNone else: if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange): result = isNone @@ -1332,12 +1366,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyTuple: if a.kind == tyTuple: result = recordRel(c, f, a) of tyObject: - if a.kind == tyObject: - if sameObjectTypes(f, a): + let effectiveArgType = if useTypeLoweringRuleInTypeClass: + a + else: + getObjectTypeOrNil(a) + if effectiveArgType == nil: return isNone + if effectiveArgType.kind == tyObject: + if sameObjectTypes(f, effectiveArgType): result = isEqual # elif tfHasMeta in f.flags: result = recordRel(c, f, a) elif trIsOutParam notin flags: - var depth = isObjectSubtype(c, a, f, nil) + var depth = isObjectSubtype(c, effectiveArgType, f, nil) if depth > 0: inc(c.inheritancePenalty, depth) result = isSubtype @@ -1533,6 +1572,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyGenericInvocation: var x = a.skipGenericAlias + if x.kind == tyGenericParam and x.len > 0: + x = x.lastSon let concpt = f[0].skipTypes({tyGenericBody}) var preventHack = concpt.kind == tyConcept if x.kind == tyOwned and f[0].kind != tyOwned: @@ -1543,7 +1584,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.calleeSym != nil and c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack: let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f) - #echo "inferred ", typeToString(inst), " for ", f return typeRel(c, inst, a, flags) if x.kind == tyGenericInvocation: @@ -1572,9 +1612,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, var fskip = skippedNone let aobj = x.skipToObject(askip) let fobj = genericBody.lastSon.skipToObject(fskip) - var depth = -1 - if fobj != nil and aobj != nil and askip == fskip: - depth = isObjectSubtype(c, aobj, fobj, f) result = typeRel(c, genericBody, x, flags) if result != isNone: # see tests/generics/tgeneric3.nim for an example that triggers this @@ -1600,7 +1637,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, put(c, key, x) elif typeRel(c, old, x, flags + {trDontBind}) == isNone: return isNone - + var depth = -1 + if fobj != nil and aobj != nil and askip == fskip: + depth = isObjectSubtype(c, aobj, fobj, f) + if result == isNone: # Here object inheriting from generic/specialized generic object # crossing path with metatypes/aliases, so we need to separate them @@ -1662,8 +1702,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, considerPreviousT: let target = f[0] let targetKind = target.kind - let effectiveArgType = a.skipTypes({tyRange, tyGenericInst, - tyBuiltInTypeClass, tyAlias, tySink, tyOwned}) + var effectiveArgType = a.getObjectTypeOrNil() + if effectiveArgType == nil: return isNone + effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass}) if targetKind == effectiveArgType.kind: if effectiveArgType.isEmptyContainer: return isNone @@ -1785,7 +1826,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if tfWildcard in a.flags: a.sym.transitionGenericParamToType() a.flags.excl tfWildcard - else: + elif doBind: + # The mechanics of `doBind` being a flag that also denotes sig cmp via + # negation is potentially problematic. `IsNone` is appropriate for + # preventing illegal bindings, but it is not necessarily appropriate + # before the bindings have been finalized. concrete = concreteType(c, a, f) if concrete == nil: return isNone diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index d05a693bb994..41d463ff8b21 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -919,6 +919,7 @@ The concept matches if: a) all expressions within the body can be compiled for the tested type b) all statically evaluable boolean expressions in the body are true +c) all type modifiers specified match their respective definitions The identifiers following the `concept` keyword represent instances of the currently matched type. You can apply any of the standard type modifiers such diff --git a/tests/overload/tor_isnt_better.nim b/tests/overload/tor_isnt_better.nim index 5ef8bc7c4cc3..ce92009d03fb 100644 --- a/tests/overload/tor_isnt_better.nim +++ b/tests/overload/tor_isnt_better.nim @@ -1,18 +1,19 @@ -discard """ - errormsg: "ambiguous call;" - line: 16 -""" - -# bug #8568 - type D[T] = object E[T] = object -proc g(a: D|E): string = "foo D|E" -proc g(a: D): string = "foo D" +block: # PR #22261 + proc d(x: D):bool= false + proc d(x: int | D[SomeInteger]):bool= true + doAssert d(D[5]()) == false -proc test() = - let x = g D[int]() -test() +block: # bug #8568 +#[ + Since PR #22261 and amendment has been made. Since D is a subset of D | E but + not the other way around `checkGeneric` should favor proc g(a: D) instead + of asserting ambiguity +]# + proc g(a: D|E): string = "foo D|E" + proc g(a: D): string = "foo D" + doAssert g(D[int]()) == "foo D" diff --git a/tests/stdlib/t21406.nim b/tests/stdlib/t21406.nim index 5b96227ce67b..86bf7b0c7c7c 100644 --- a/tests/stdlib/t21406.nim +++ b/tests/stdlib/t21406.nim @@ -1,5 +1,7 @@ import std/[times, strformat] import std/assertions -doAssert fmt"{getTime()}" == $getTime() -doAssert fmt"{now()}" == $now() +let aTime = getTime() +doAssert fmt"{aTime}" == $aTime +let aNow = now() +doAssert fmt"{aNow}" == $aNow From c3b95cbd2c944512585b843df7f0920894cf16ae Mon Sep 17 00:00:00 2001 From: daylin <47667941+daylinmorgan@users.noreply.github.com> Date: Sat, 30 Sep 2023 07:53:09 -0500 Subject: [PATCH 322/347] docs: add another switch example for nimscript (#22772) I couldn't find any documentation on the syntax for --hint:X:on|off with `nimscript` except in [this old forum post](https://forum.nim-lang.org/t/8526#55236). --- doc/nims.md | 2 ++ lib/system/nimscript.nim | 1 + 2 files changed, 3 insertions(+) diff --git a/doc/nims.md b/doc/nims.md index 01cdf47c3541..42cc6e124d1f 100644 --- a/doc/nims.md +++ b/doc/nims.md @@ -124,6 +124,8 @@ Here are few examples of using the `switch` proc: switch("define", "release") # command-line: --forceBuild switch("forceBuild") + # command-line: --hint[Conf]:off or --hint:Conf:off + switch("hint", "[Conf]:off") ``` NimScripts also support `--`:option: templates for convenience, which look diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index a2c897b04b56..bdf6145a116b 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -161,6 +161,7 @@ template `--`*(key, val: untyped) = ## ```nim ## --path:somePath # same as switch("path", "somePath") ## --path:"someOtherPath" # same as switch("path", "someOtherPath") + ## --hint:"[Conf]:off" # same as switch("hint", "[Conf]:off") ## ``` switch(strip(astToStr(key)), strip(astToStr(val))) From b60f15e0dcfd514521bfc2e33c6f2728f565bc9d Mon Sep 17 00:00:00 2001 From: Juan Carlos <juancarlospaco@gmail.com> Date: Sun, 1 Oct 2023 02:19:37 -0300 Subject: [PATCH 323/347] copyFile with POSIX_FADV_SEQUENTIAL (#22776) - Continuation of https://github.com/nim-lang/Nim/pull/22769 - See https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html - The code was already there in `std/posix` since years ago. 3 line diff. --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- changelog.md | 1 + lib/std/private/osfiles.nim | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/changelog.md b/changelog.md index 1fb6377a2848..5656a86c3034 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ [//]: # "Changes:" - Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one. +- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads. [//]: # "Additions:" diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index b7957fa4f1ba..948df4211fbe 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -203,6 +203,7 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 1 ## `-d:nimLegacyCopyFile` is used. ## ## `copyFile` allows to specify `bufferSize` to improve I/O performance. + ## ## See also: ## * `CopyFlag enum`_ ## * `copyDir proc`_ @@ -243,6 +244,13 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 1 if not open(d, dest, fmWrite): close(s) raiseOSError(osLastError(), dest) + + # Hints for kernel-level aggressive sequential low-fragmentation read-aheads: + # https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html + when defined(linux) or defined(osx): + discard posix_fadvise(getFileHandle(d), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL) + discard posix_fadvise(getFileHandle(s), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL) + var buf = alloc(bufferSize) while true: var bytesread = readBuffer(s, buf, bufferSize) From 49df69334e8c81354d604bbde05665d6c5450ddb Mon Sep 17 00:00:00 2001 From: CMD <48290258+cmd410@users.noreply.github.com> Date: Sun, 1 Oct 2023 08:20:43 +0300 Subject: [PATCH 324/347] Fix IndexDefect in asyncfile.readLine (#22774) `readLine` proc in asyncfile module caused IndexDefect when it reached EoF. Now it returns empty string instead. --- lib/pure/asyncfile.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 96d5e6d2e72d..118f94748616 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -301,6 +301,8 @@ proc readLine*(f: AsyncFile): Future[string] {.async.} = result = "" while true: var c = await read(f, 1) + if c.len == 0: + break if c[0] == '\c': c = await read(f, 1) break From 642ac0c1c31063ae966d8448c64c539203432e94 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 3 Oct 2023 02:45:04 +0800 Subject: [PATCH 325/347] fixes #22753; Nimsuggest segfault with invalid assignment to table (#22781) fixes #22753 ## Future work We should turn all the error nodes into nodes of a nkError kind, which could be a industrious task. But perhaps we can add a special treatment for error nodes to make the transition smooth. --- compiler/semexprs.nim | 2 +- compiler/semmagic.nim | 2 +- tests/errmsgs/t10735.nim | 15 ++++++---- tests/errmsgs/t22753.nim | 35 ++++++++++++++++++++++ tests/generics/tuninstantiated_failure.nim | 4 +-- 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 tests/errmsgs/t22753.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 633a0cc26758..8cdd16f02a74 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1886,7 +1886,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result.add(n[1]) if mode == noOverloadedSubscript: bracketNotFoundError(c, result) - return n + return errorNode(c, n) else: result = semExprNoType(c, result) return result diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index cb5e76e8c672..b5990161ebc1 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -66,7 +66,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info) bracketNotFoundError(c, x) #localError(c.config, n.info, "could not resolve: " & $n) - result = n + result = errorNode(c, n) proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = # rewrite `[]=`(a, i, x) back to ``a[i] = x``. diff --git a/tests/errmsgs/t10735.nim b/tests/errmsgs/t10735.nim index 307acac2d45b..f480d35ac8dd 100644 --- a/tests/errmsgs/t10735.nim +++ b/tests/errmsgs/t10735.nim @@ -1,10 +1,12 @@ discard """ cmd: "nim check $file" - errormsg: "selector must be of an ordinal type, float or string" + errormsg: "illformed AST: case buf[pos]" nimout: ''' -t10735.nim(38, 5) Error: 'let' symbol requires an initialization -t10735.nim(39, 10) Error: undeclared identifier: 'pos' -t10735.nim(39, 9) Error: type mismatch: got <cstring, > +t10735.nim(43, 5) Error: 'let' symbol requires an initialization +t10735.nim(44, 10) Error: undeclared identifier: 'pos' +t10735.nim(44, 10) Error: expression 'pos' has no type (or is ambiguous) +t10735.nim(44, 10) Error: expression 'pos' has no type (or is ambiguous) +t10735.nim(44, 9) Error: type mismatch: got <cstring, > but expected one of: proc `[]`(s: string; i: BackwardsIndex): char first type mismatch at position: 0 @@ -26,11 +28,14 @@ proc `[]`[T](s: openArray[T]; i: BackwardsIndex): T first type mismatch at position: 0 proc `[]`[T](s: var openArray[T]; i: BackwardsIndex): var T first type mismatch at position: 0 +template `[]`(a: WideCStringObj; idx: int): Utf16Char + first type mismatch at position: 0 template `[]`(s: string; i: int): char first type mismatch at position: 0 expression: `[]`(buf, pos) -t10735.nim(39, 9) Error: selector must be of an ordinal type, float or string +t10735.nim(44, 9) Error: expression '' has no type (or is ambiguous) +t10735.nim(46, 3) Error: illformed AST: case buf[pos] ''' joinable: false """ diff --git a/tests/errmsgs/t22753.nim b/tests/errmsgs/t22753.nim new file mode 100644 index 000000000000..af6a871f13a6 --- /dev/null +++ b/tests/errmsgs/t22753.nim @@ -0,0 +1,35 @@ +discard """ +cmd: "nim check --hints:off $file" +errormsg: "type mismatch" +nimoutFull: true +nimout: ''' +t22753.nim(34, 13) Error: array expects two type parameters +t22753.nim(35, 1) Error: expression 'x' has no type (or is ambiguous) +t22753.nim(35, 1) Error: expression 'x' has no type (or is ambiguous) +t22753.nim(35, 2) Error: type mismatch: got <> +but expected one of: +proc `[]=`(s: var string; i: BackwardsIndex; x: char) + first type mismatch at position: 0 +proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: sink S) + first type mismatch at position: 0 +proc `[]=`[Idx, T; U, V: Ordinal](a: var array[Idx, T]; x: HSlice[U, V]; + b: openArray[T]) + first type mismatch at position: 0 +proc `[]=`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) + first type mismatch at position: 0 +proc `[]=`[T, U: Ordinal](s: var string; x: HSlice[T, U]; b: string) + first type mismatch at position: 0 +proc `[]=`[T; U, V: Ordinal](s: var seq[T]; x: HSlice[U, V]; b: openArray[T]) + first type mismatch at position: 0 +proc `[]=`[T](s: var openArray[T]; i: BackwardsIndex; x: T) + first type mismatch at position: 0 +template `[]=`(a: WideCStringObj; idx: int; val: Utf16Char) + first type mismatch at position: 0 +template `[]=`(s: string; i: int; val: char) + first type mismatch at position: 0 + +expression: `[]=`(x, 0, 9) +''' +""" +var x: array[3] # bug #22753 +x[0] = 9 \ No newline at end of file diff --git a/tests/generics/tuninstantiated_failure.nim b/tests/generics/tuninstantiated_failure.nim index f09b115d65a0..f3d5b34b8979 100644 --- a/tests/generics/tuninstantiated_failure.nim +++ b/tests/generics/tuninstantiated_failure.nim @@ -11,6 +11,6 @@ func `[]`[T, K](x: var Test[T, K], idx: int): var Test[T, K] = x var b: Something -# Should give a type-mismatch since Something isn't a valid Test +# Should give an error since Something isn't a valid Test b[0].name = "Test" #[tt.Error - ^ type mismatch]# + ^ expression '' has no type (or is ambiguous)]# From 5d5c39e7452af71162c6d9b499dffe86d9c2d485 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:00:24 +0800 Subject: [PATCH 326/347] fixes #22778 regression: contentLength implementation type mismatched (#22780) fixes #22778 follow up https://github.com/nim-lang/Nim/pull/19835 --- lib/pure/httpclient.nim | 2 +- tests/stdlib/tasynchttpserver.nim | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index ddf208e4e362..9444618db652 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -305,7 +305,7 @@ proc contentLength*(response: Response | AsyncResponse): int = ## ## A `ValueError` exception will be raised if the value is not an integer. ## If the Content-Length header is not set in the response, ContentLength is set to the value -1. - var contentLengthHeader = response.headers.getOrDefault("Content-Length", @["-1"]) + var contentLengthHeader = response.headers.getOrDefault("Content-Length", HttpHeaderValues(@["-1"])) result = contentLengthHeader.parseInt() proc lastModified*(response: Response | AsyncResponse): DateTime = diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim index 8cf0d0cedbee..5a7e2da4018a 100644 --- a/tests/stdlib/tasynchttpserver.nim +++ b/tests/stdlib/tasynchttpserver.nim @@ -109,6 +109,7 @@ proc testCustomContentLength() {.async.} = doAssert(body == "") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "0") + doAssert contentLength(response) == 0 # bug #22778 runTest(handler, request, test) From db36765afd41fac8b30d0564f0f86e7315bd781d Mon Sep 17 00:00:00 2001 From: Pylgos <43234674+Pylgos@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:22:31 +0900 Subject: [PATCH 327/347] nimsuggest: Clear generic inst cache before partial recompilation (#22783) fixes #19371 fixes #21093 fixes #22119 --- nimsuggest/nimsuggest.nim | 24 +++++++++++++++++++++--- nimsuggest/tests/tgenerics.nim | 18 ++++++++++++++++++ nimsuggest/tests/tv3_generics.nim | 18 ++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 nimsuggest/tests/tgenerics.nim create mode 100644 nimsuggest/tests/tv3_generics.nim diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 649ece06b28d..28faf9e3c39e 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -193,6 +193,24 @@ template benchmark(benchmarkName: untyped, code: untyped) = let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3) myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s" +proc clearInstCache(graph: ModuleGraph, projectFileIdx: FileIndex) = + if projectFileIdx == InvalidFileIdx: + graph.typeInstCache.clear() + graph.procInstCache.clear() + return + var typeIdsToDelete = newSeq[ItemId]() + for id in graph.typeInstCache.keys: + if id.module == projectFileIdx.int: + typeIdsToDelete.add id + for id in typeIdsToDelete: + graph.typeInstCache.del id + var procIdsToDelete = newSeq[ItemId]() + for id in graph.procInstCache.keys: + if id.module == projectFileIdx.int: + procIdsToDelete.add id + for id in procIdsToDelete: + graph.procInstCache.del id + proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, tag: string, graph: ModuleGraph) = let conf = graph.config @@ -221,6 +239,7 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, if conf.suggestVersion == 1: graph.usageSym = nil if not isKnownFile: + graph.clearInstCache(dirtyIdx) graph.compileProject(dirtyIdx) if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and dirtyfile.isEmpty: @@ -231,6 +250,7 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, graph.markClientsDirty dirtyIdx if conf.ideCmd != ideMod: if isKnownFile: + graph.clearInstCache(modIdx) graph.compileProject(modIdx) if conf.ideCmd in {ideUse, ideDus}: let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym @@ -716,9 +736,7 @@ proc recompilePartially(graph: ModuleGraph, projectFileIdx = InvalidFileIdx) = # inst caches are breaking incremental compilation when the cache caches stuff # from dirty buffer - # TODO: investigate more efficient way to achieve the same - # graph.typeInstCache.clear() - # graph.procInstCache.clear() + graph.clearInstCache(projectFileIdx) GC_fullCollect() diff --git a/nimsuggest/tests/tgenerics.nim b/nimsuggest/tests/tgenerics.nim new file mode 100644 index 000000000000..7f490321c01e --- /dev/null +++ b/nimsuggest/tests/tgenerics.nim @@ -0,0 +1,18 @@ +type + Hello[T] = object + value: T + +proc printHelloValue[T](hello: Hello[T]) = + echo hello.value + +proc main() = + let a = Hello[float]() + p#[!]#rintHelloValue(a) + +main() + +discard """ +$nimsuggest --tester $file +>def $1 +def;;skProc;;tgenerics.printHelloValue;;proc (hello: Hello[printHelloValue.T]);;$file;;5;;5;;"";;100 +""" diff --git a/nimsuggest/tests/tv3_generics.nim b/nimsuggest/tests/tv3_generics.nim new file mode 100644 index 000000000000..2bfb2ca1d655 --- /dev/null +++ b/nimsuggest/tests/tv3_generics.nim @@ -0,0 +1,18 @@ +type + Hello[T] = object + value: T + +proc printHelloValue[T](hello: Hello[T]) = + echo hello.value + +proc main() = + let a = Hello[float]() + p#[!]#rintHelloValue(a) + +main() + +discard """ +$nimsuggest --v3 --tester $file +>def $1 +def;;skProc;;tv3_generics.printHelloValue;;proc (hello: Hello[printHelloValue.T]);;$file;;5;;5;;"";;100 +""" From 1a6ca0c6047ac1f1489f2b45e4966ddca1f40e46 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 3 Oct 2023 18:46:41 +0800 Subject: [PATCH 328/347] arraymancer switches to the offical URL (#22782) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index f769c4ebda36..5f5868cfd6ff 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -35,7 +35,7 @@ proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailu pkg "alea" pkg "argparse" -pkg "arraymancer", "nim c tests/tests_cpu.nim", url = "https://github.com/nim-lang/Arraymancer" +pkg "arraymancer", "nim c tests/tests_cpu.nim" pkg "ast_pattern_matching", "nim c -r tests/test1.nim" pkg "asyncftpclient", "nimble compileExample" pkg "asyncthreadpool", "nimble test --mm:refc" From f4a623dadf1a5ba287dbb55893ab70d1d6638c3f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:00:41 +0800 Subject: [PATCH 329/347] document `atomicInc` and `atomicDec` (#22789) --- lib/std/sysatomics.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/std/sysatomics.nim b/lib/std/sysatomics.nim index 36a4e55378f7..d2f7f59eb58c 100644 --- a/lib/std/sysatomics.nim +++ b/lib/std/sysatomics.nim @@ -265,6 +265,7 @@ else: proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} = + ## Atomically increments the integer by some `x`. It returns the new value. when someGcc and hasThreadSupport: result = atomicAddFetch(memLoc.addr, x, ATOMIC_SEQ_CST) elif someVcc and hasThreadSupport: @@ -275,6 +276,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: result = memLoc proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} = + ## Atomically decrements the integer by some `x`. It returns the new value. when someGcc and hasThreadSupport: when declared(atomicSubFetch): result = atomicSubFetch(memLoc.addr, x, ATOMIC_SEQ_CST) From f2f0b3e25d976446e657cee7157591ce587624fd Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 5 Oct 2023 01:41:39 +0800 Subject: [PATCH 330/347] fixes #22711; Check atomicArc for atomic destroy race condition (#22788) fixes #22711 Per @elcritch's awesome solution --- lib/system/arc.nim | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 0624bd4e3af6..b49a6a63b209 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -202,15 +202,22 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = writeStackTrace() cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count) - if cell.count == 0: - result = true - when traceCollector: - cprintf("[ABOUT TO DESTROY] %p\n", cell) + when defined(gcAtomicArc) and hasThreadSupport: + # `atomicDec` returns the new value + if atomicDec(cell.rc, rcIncrement) == -1: + result = true + when traceCollector: + cprintf("[ABOUT TO DESTROY] %p\n", cell) else: - decrement cell - # According to Lins it's correct to do nothing else here. - when traceCollector: - cprintf("[DECREF] %p\n", cell) + if cell.count == 0: + result = true + when traceCollector: + cprintf("[ABOUT TO DESTROY] %p\n", cell) + else: + decrement cell + # According to Lins it's correct to do nothing else here. + when traceCollector: + cprintf("[DECREF] %p\n", cell) proc GC_unref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. From 476492583b5b40c184df015142afcd672b09330b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 7 Oct 2023 04:39:32 +0800 Subject: [PATCH 331/347] remove the O(n*n) `addUnique` one from std (#22799) It's not used in the compiler, besides, it doesn't seem to be a common operation. Follows the discussion on the Discord. --- lib/pure/collections/sequtils.nim | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 9c33b2eb6c76..a32060e183cc 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -157,18 +157,6 @@ func addUnique*[T](s: var seq[T], x: sink T) = else: s.add x -func addUnique*[T](s: var seq[T], xs: sink seq[T]) = - ## Adds any items from `xs` to the container `s` that are not already present. - ## Uses `==` to check if the item is already present. - runnableExamples: - var a = @[1, 2, 3] - a.addUnique(@[3, 4]) - a.addUnique(@[4, 5]) - assert a == @[1, 2, 3, 4, 5] - - for i in 0..high(xs): - addUnique(s, move(xs[i])) - func count*[T](s: openArray[T], x: T): int = ## Returns the number of occurrences of the item `x` in the container `s`. ## From f111009e5dadceeab2fb076c968f8e18a5e495f8 Mon Sep 17 00:00:00 2001 From: Levi Notik <levinotik@users.noreply.github.com> Date: Sat, 7 Oct 2023 01:43:17 -0400 Subject: [PATCH 332/347] Fix typo/grammar in exception tracking section (#22801) I came across this sentence in the Nim Manual and couldn't make sense of it. I believe this is the correct fix for the sentence. --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- doc/manual.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.md b/doc/manual.md index fba905bb9c15..8f419705efd3 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -5160,7 +5160,7 @@ possibly raised exceptions; the algorithm operates on `p`'s call graph: raise `system.Exception` (the base type of the exception hierarchy) and thus any exception unless `T` has an explicit `raises` list. However, if the call is of the form `f(...)` where `f` is a parameter of - the currently analyzed routine it is ignored that is marked as `.effectsOf: f`. + the currently analyzed routine that is marked as `.effectsOf: f`, it is ignored. The call is optimistically assumed to have no effect. Rule 2 compensates for this case. 2. Every expression `e` of some proc type within a call that is passed to parameter From efa64aa49b11e93461626e84b9f67b6185f26e1f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 7 Oct 2023 13:43:39 +0800 Subject: [PATCH 333/347] fixes #22787; marks `var section` in the loop as reassign preventing cursor (#22800) fixes #22787 --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- compiler/varpartitions.nim | 4 ++++ tests/arc/t22787.nim | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/arc/t22787.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 74bf63da8ff3..957497bb6255 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -823,6 +823,10 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = registerVariable(c, child[i]) #deps(c, child[i], last) + if c.inLoop > 0 and child[0].kind == nkSym: # bug #22787 + let vid = variableId(c, child[0].sym) + if child[^1].kind != nkEmpty: + markAsReassigned(c, vid) of nkAsgn, nkFastAsgn, nkSinkAsgn: computeLiveRanges(c, n[0]) computeLiveRanges(c, n[1]) diff --git a/tests/arc/t22787.nim b/tests/arc/t22787.nim new file mode 100644 index 000000000000..5840a984b9f9 --- /dev/null +++ b/tests/arc/t22787.nim @@ -0,0 +1,37 @@ +discard """ + joinable: false +""" + +import std/assertions + +proc foo = + var s:seq[string] + var res = "" + + for i in 0..3: + s.add ("test" & $i) + s.add ("test" & $i) + + var lastname:string + + for i in s: + var name = i[0..4] + + if name != lastname: + res.add "NEW:" & name & "\n" + else: + res.add name & ">" & lastname & "\n" + + lastname = name + + doAssert res == """ +NEW:test0 +test0>test0 +NEW:test1 +test1>test1 +NEW:test2 +test2>test2 +NEW:test3 +test3>test3 +""" +foo() \ No newline at end of file From 5bcea05cafb2a655ed1dd44544998eac5bed7c7f Mon Sep 17 00:00:00 2001 From: Matt Rixman <5834582+MatrixManAtYrService@users.noreply.github.com> Date: Sun, 8 Oct 2023 16:56:18 +0000 Subject: [PATCH 334/347] Add getCursorPos() to std/terminal (#22749) This would be handy for making terminal apps which display content below the prompt (e.g. `fzf` does this). Need to test it on windows before I remove "draft" status. --------- Co-authored-by: Matt Rixman <MatrixManAtYrService@users.noreply.github.com> Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- lib/pure/terminal.nim | 48 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 4177eb002ad8..da95ac32ae30 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -60,7 +60,7 @@ runnableExamples("-r:off"): import macros import strformat -from strutils import toLowerAscii, `%` +from strutils import toLowerAscii, `%`, parseInt import colors when defined(windows): @@ -96,6 +96,7 @@ const fgPrefix = "\e[38;2;" bgPrefix = "\e[48;2;" ansiResetCode* = "\e[0m" + getPos = "\e[6n" stylePrefix = "\e[" when defined(windows): @@ -220,6 +221,9 @@ when defined(windows): raiseOSError(osLastError()) return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) + proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError, OSError].} = + return getCursorPos(getStdHandle(STD_OUTPUT_HANDLE)) + proc setCursorPos(h: Handle, x, y: int) = var c: COORD c.x = int16(x) @@ -267,6 +271,48 @@ else: mode.c_cc[VTIME] = 0.cuchar discard fd.tcSetAttr(time, addr mode) + proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError].} = + ## Returns cursor position (x, y) + ## writes to stdout and expects the terminal to respond via stdin + var + xStr = "" + yStr = "" + ch: char + ct: int + readX = false + + # use raw mode to ask terminal for cursor position + let fd = getFileHandle(stdin) + var oldMode: Termios + discard fd.tcGetAttr(addr oldMode) + fd.setRaw() + stdout.write(getPos) + flushFile(stdout) + + try: + # parse response format: [yyy;xxxR + while true: + let n = readBuffer(stdin, addr ch, 1) + if n == 0 or ch == 'R': + if xStr == "" or yStr == "": + raise newException(ValueError, "Got character position message that was missing data") + break + ct += 1 + if ct > 16: + raise newException(ValueError, "Got unterminated character position message from terminal") + if ch == ';': + readX = true + elif ch in {'0'..'9'}: + if readX: + xStr.add(ch) + else: + yStr.add(ch) + finally: + # restore previous terminal mode + discard fd.tcSetAttr(TCSADRAIN, addr oldMode) + + return (parseInt(xStr), parseInt(yStr)) + proc terminalWidthIoctl*(fds: openArray[int]): int = ## Returns terminal width from first fd that supports the ioctl. From c3774c8821cc25187252491b4514235b9a8f1aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Sun, 8 Oct 2023 19:22:35 +0100 Subject: [PATCH 335/347] fixes nimsuggest false error on lifetime tracking hook fixes #22794 (#22805) fixes #22794 Not sure if it's too much. --- compiler/modulegraphs.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index baedad0af46f..4dadb45b6ccf 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -582,6 +582,7 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = if m != nil: g.suggestSymbols.del(fileIdx) g.suggestErrors.del(fileIdx) + g.resetForBackend incl m.flags, sfDirty proc unmarkAllDirty*(g: ModuleGraph) = From 8ac466980f7658c37b05c6ec099537ea0e459cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Sun, 8 Oct 2023 22:51:44 +0100 Subject: [PATCH 336/347] marking a field with noInit allows to skip constructor initialiser (#22802) Co-authored-by: Andreas Rumpf <rumpf_a@web.de> --- changelog.md | 2 +- compiler/ccgtypes.nim | 3 ++- compiler/pragmas.nim | 4 +-- doc/manual_experimental.md | 50 ++++++++++++++++++++++++++++++++++++++ tests/cpp/tnoinitfield.nim | 30 +++++++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 tests/cpp/tnoinitfield.nim diff --git a/changelog.md b/changelog.md index 5656a86c3034..72d0a2a2dd7a 100644 --- a/changelog.md +++ b/changelog.md @@ -28,7 +28,7 @@ slots when enlarging a sequence. ## Language changes - +- `noInit` can be used in types and fields to disable member initializers in the C++ backend. ## Compiler changes diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1aed8442bf24..a5555048f004 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -735,7 +735,8 @@ proc genRecordFieldsAux(m: BModule; n: PNode, else: # don't use fieldType here because we need the # tyGenericInst for C++ template support - if fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ): + let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags) + if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)): var initializer = genCppInitializer(m, nil, fieldType) result.addf("\t$1$3 $2$4;$n", [getTypeDescAux(m, field.loc.t, check, dkField), sname, noAlias, initializer]) else: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 5fb74406bea7..e6867aa5d286 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -72,9 +72,9 @@ const wIncompleteStruct, wCompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl, - wSendable} + wSendable, wNoInit} fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor, - wRequiresInit, wNoalias, wAlign} - {wExportNims, wNodecl} # why exclude these? + wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, wNoInit, wCompileTime, wGlobal, diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 41d463ff8b21..249f9367b350 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2408,6 +2408,56 @@ proc makeCppStruct(a: cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "Cp # If one removes a default value from the constructor and passes it to the call explicitly, the C++ compiler will complain. ``` +Skip initializers in fields members +=================================== + +By using `noInit` in a type or field declaration, the compiler will skip the initializer. By doing so one can explicitly initialize those values in the constructor of the type owner. + +For example: + +```nim + +{.emit: """/*TYPESECTION*/ + struct Foo { + Foo(int a){}; + }; + struct Boo { + Boo(int a){}; + }; + + """.} + +type + Foo {.importcpp.} = object + Boo {.importcpp, noInit.} = object + Test {.exportc.} = object + foo {.noInit.}: Foo + boo: Boo + +proc makeTest(): Test {.constructor: "Test() : foo(10), boo(1)".} = + discard + +proc main() = + var t = makeTest() + +main() + +``` + +Will produce: + +```c++ + +struct Test { + Foo foo; + Boo boo; + N_LIB_PRIVATE N_NOCONV(, Test)(void); +}; + +``` + +Notice that without `noInit` it would produce `Foo foo {}` and `Boo boo {}` + Member pragma ============= diff --git a/tests/cpp/tnoinitfield.nim b/tests/cpp/tnoinitfield.nim new file mode 100644 index 000000000000..4deffece827d --- /dev/null +++ b/tests/cpp/tnoinitfield.nim @@ -0,0 +1,30 @@ +discard """ + targets: "cpp" + cmd: "nim cpp $file" + output: ''' +''' +""" +{.emit: """/*TYPESECTION*/ + struct Foo { + Foo(int a){}; + }; + struct Boo { + Boo(int a){}; + }; + + """.} + +type + Foo {.importcpp.} = object + Boo {.importcpp, noInit.} = object + Test {.exportc.} = object + foo {.noInit.}: Foo + boo: Boo + +proc makeTest(): Test {.constructor: "Test() : foo(10), boo(1)".} = + discard + +proc main() = + var t = makeTest() + +main() \ No newline at end of file From 81b2ae747e307f4ab171d3b62a0e6ea8b6a81d3d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:36:56 +0800 Subject: [PATCH 337/347] fixes #8893; guard against array access in renderer (#22807) fixes #8893 --- compiler/renderer.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 41078716b11c..3a6d0ae6502e 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -864,7 +864,7 @@ proc gproc(g: var TSrcGen, n: PNode) = if renderNoPragmas notin g.flags: gsub(g, n[pragmasPos]) if renderNoBody notin g.flags: - if n[bodyPos].kind != nkEmpty: + if n.len > bodyPos and n[bodyPos].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") indentNL(g) From bf72d87f249221ed6623321f3cca0de9b35e0e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= <info@jmgomez.me> Date: Wed, 11 Oct 2023 07:28:00 +0100 Subject: [PATCH 338/347] adds support for noDecl in constructor (#22811) Notice the test wouldnt link before --- compiler/ccgtypes.nim | 1 + doc/manual_experimental.md | 2 +- tests/cpp/tconstructor.nim | 11 ++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index a5555048f004..2f92cb12b02a 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -757,6 +757,7 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = isCtorGen = true if prc.typ.n.len == 1: isDefaultCtorGen = true + if lfNoDecl in prc.loc.flags: continue genMemberProcHeader(m, prc, header, false, true) result.addf "$1;$n", [header] if isCtorGen and not isDefaultCtorGen: diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 249f9367b350..4bafd408ff0b 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -2345,7 +2345,7 @@ proc makeFoo(x: int32): Foo {.constructor.} = result.x = x ``` -It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. +It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. One can avoid this behaviour by using `noDecl` in a default constructor. Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints. diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim index 8c5d4dca2dd2..ac73b78e663c 100644 --- a/tests/cpp/tconstructor.nim +++ b/tests/cpp/tconstructor.nim @@ -16,6 +16,7 @@ ___ 777 10 123 +() ''' """ @@ -106,4 +107,12 @@ proc init = n.x = 123 echo n.x -init() \ No newline at end of file +init() + +#tests that the ctor is not declared with nodecl. +#nodelc also prevents the creation of a default one when another is created. +type Foo {.exportc.} = object + +proc makeFoo(): Foo {.used, constructor, nodecl.} = discard + +echo $Foo() \ No newline at end of file From 2cf214d6d46d11024e6420520e39eed779da284e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 11 Oct 2023 18:06:42 +0800 Subject: [PATCH 339/347] allows cast int to bool/enum in VM (#22809) Since they are integer types, by mean of allowing cast integer to enums in VM, we can suppress some enum warnings in the stdlib in the unified form, namely using a cast expression. --- compiler/vmgen.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 2e54812a0564..03265f5b559e 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -904,9 +904,9 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = - const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} + const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool} var signedIntegers = {tyInt..tyInt64} - var unsignedIntegers = {tyUInt..tyUInt64, tyChar} + var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool} let src = n[1].typ.skipTypes(abstractRange)#.kind let dst = n[0].typ.skipTypes(abstractRange)#.kind let srcSize = getSize(c.config, src) From 14d25eedfd5ada29fcd3690919e40623563b98fa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 11 Oct 2023 19:27:22 +0800 Subject: [PATCH 340/347] suppress incorrect var T destructor warnings for newFinalizer in stdlib (#22810) in `std/nre` ```nim proc initRegex(pattern: string, flags: int, study = true): Regex = new(result, destroyRegex) ``` gives incorrect warnings like ``` C:\Users\blue\Documents\Nim\lib\impure\nre.nim(252, 6) Error: A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter [Deprecated ``` --- compiler/semmagic.nim | 4 +++- compiler/semstmts.nim | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index b5990161ebc1..6f2ddd95a631 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -526,7 +526,9 @@ proc semNewFinalize(c: PContext; n: PNode): PNode = selfPtr.add transFormedSym.ast[bodyPos][1] selfPtr.typ = selfSymbolType transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr) - bindTypeHook(c, transFormedSym, n, attachedDestructor) + # TODO: suppress var destructor warnings; if newFinalizer is not + # TODO: deprecated, try to implement plain T destructor + bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true) result = addDefaultFieldForNew(c, n) proc semPrivateAccess(c: PContext, n: PNode): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2df1c42b3d1e..a4de874ba9df 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1904,7 +1904,7 @@ proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = incl(s.flags, sfUsed) incl(s.flags, sfOverridden) -proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = +proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) = let t = s.typ var noError = false let cond = case op @@ -1923,7 +1923,7 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = elif obj.kind == tyGenericInvocation: obj = obj[0] else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: - if op == attachedDestructor and t[1].kind == tyVar: + if (not suppressVarDestructorWarning) and op == attachedDestructor and t[1].kind == tyVar: message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter") obj = canonType(c, obj) let ao = getAttachedOp(c.graph, obj, op) From 9d7acd001f268e2b9dab9228ca5e22bf75028704 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:14:12 +0800 Subject: [PATCH 341/347] use lent for the return value of index accesses of tables (#22812) ref https://forum.nim-lang.org/t/10525 --- lib/pure/collections/tables.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 39dcddb5a9c0..6ea4a9a45eaa 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -312,7 +312,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = result = initTable[A, B](pairs.len) for key, val in items(pairs): result[key] = val -proc `[]`*[A, B](t: Table[A, B], key: A): B = +proc `[]`*[A, B](t: Table[A, B], key: A): lent B = ## Retrieves the value at `t[key]`. ## ## If `key` is not in `t`, the `KeyError` exception is raised. @@ -1357,7 +1357,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = result = initOrderedTable[A, B](pairs.len) for key, val in items(pairs): result[key] = val -proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = +proc `[]`*[A, B](t: OrderedTable[A, B], key: A): lent B = ## Retrieves the value at `t[key]`. ## ## If `key` is not in `t`, the `KeyError` exception is raised. From ecaccafa6c18e0b92cd5f75d3363d61c0866dec9 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 11 Oct 2023 23:18:54 +0800 Subject: [PATCH 342/347] =?UTF-8?q?fixes=20#22790;=20use=20cast=20suppress?= =?UTF-8?q?=20AnyEnumConv=20warnings=20for=20enums=20withou=E2=80=A6=20(#2?= =?UTF-8?q?2813)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …t holes fixes #22790 --- compiler/condsyms.nim | 2 ++ lib/system/iterators_1.nim | 17 +++++++++++++---- tests/parallel/tuseafterdef.nim | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 81081fd93bb1..520545a81534 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -160,3 +160,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimUseStrictDefs") defineSymbol("nimHasNolineTooLong") + + defineSymbol("nimHasCastExtendedVm") diff --git a/lib/system/iterators_1.nim b/lib/system/iterators_1.nim index be61fd62ce17..d00e3f823f6b 100644 --- a/lib/system/iterators_1.nim +++ b/lib/system/iterators_1.nim @@ -16,7 +16,7 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} = let x = collect(newSeq): for i in countdown(7, 3): i - + assert x == @[7, 6, 5, 4, 3] let y = collect(newseq): @@ -32,7 +32,10 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} = elif T is IntLikeForCount and T is Ordinal: var res = int(a) while res >= int(b): - yield T(res) + when defined(nimHasCastExtendedVm): + yield cast[T](res) + else: + yield T(res) dec(res, step) else: var res = a @@ -64,7 +67,10 @@ iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} = when T is IntLikeForCount and T is Ordinal: var res = int(a) while res <= int(b): - yield T(res) + when defined(nimHasCastExtendedVm): + yield cast[T](res) + else: + yield T(res) inc(res, step) else: var res = a @@ -89,7 +95,10 @@ iterator `..`*[T](a, b: T): T {.inline.} = when T is IntLikeForCount and T is Ordinal: var res = int(a) while res <= int(b): - yield T(res) + when defined(nimHasCastExtendedVm): + yield cast[T](res) + else: + yield T(res) inc(res) else: var res = a diff --git a/tests/parallel/tuseafterdef.nim b/tests/parallel/tuseafterdef.nim index e73f1b7946d0..64f835a1bcee 100644 --- a/tests/parallel/tuseafterdef.nim +++ b/tests/parallel/tuseafterdef.nim @@ -1,5 +1,6 @@ discard """ matrix: "--mm:refc" + disabled: true errormsg: "(k)..(k) not disjoint from (k)..(k)" line: 24 action: compile From 816589b6674e3af281766f1279420758dcacedc4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf <rumpf_a@web.de> Date: Wed, 11 Oct 2023 17:44:14 +0200 Subject: [PATCH 343/347] NIR: Nim intermediate representation (#22777) Theoretical Benefits / Plans: - Typed assembler-like language. - Allows for a CPS transformation. - Can replace the existing C backend by a new C backend. - Can replace the VM. - Can do more effective "not nil" checking and static array bounds checking. - Can be used instead of the DFA. - Easily translatable to LLVM. - Reasonably easy to produce native code from. - Tiny memory consumption. No pointers, no cry. **In very early stages of development.** Todo: - [x] Map Nim types to IR types. - [ ] Map Nim AST to IR instructions: - [x] Map bitsets to bitops. - [ ] Implement string cases. - [ ] Implement range and index checks. - [x] Implement `default(T)` builtin. - [x] Implement multi string concat. - [ ] Write some analysis passes. - [ ] Write a backend. - [x] Integrate into the compilation pipeline. --- compiler/ast.nim | 6 +- compiler/bitsets.nim | 5 + compiler/ccgexprs.nim | 42 +- compiler/ccgreset.nim | 4 +- compiler/ccgstmts.nim | 3 +- compiler/ccgtypes.nim | 36 +- compiler/cgen.nim | 14 +- compiler/dfa.nim | 4 - compiler/expanddefaults.nim | 131 ++ compiler/ic/bitabs.nim | 2 + compiler/magicsys.nim | 9 + compiler/main.nim | 11 +- compiler/modulegraphs.nim | 2 + compiler/nim.nim | 3 +- compiler/nir/ast2ir.nim | 2189 +++++++++++++++++++++++++++++++++ compiler/nir/cir.nim | 78 ++ compiler/nir/nir.nim | 71 ++ compiler/nir/nirinsts.nim | 418 +++++++ compiler/nir/nirlineinfos.nim | 78 ++ compiler/nir/nirslots.nim | 105 ++ compiler/nir/nirtypes.nim | 347 ++++++ compiler/nir/types2ir.nim | 466 +++++++ compiler/pipelines.nim | 8 +- compiler/semobjconstr.nim | 16 +- compiler/semparallel.nim | 2 +- compiler/trees.nim | 4 - compiler/vm.nim | 2 +- compiler/vmgen.nim | 4 - lib/std/formatfloat.nim | 31 +- lib/system.nim | 15 +- lib/system/ansi_c.nim | 2 +- lib/system/cgprocs.nim | 10 - lib/system/memory.nim | 1 + lib/system/strmantle.nim | 8 + lib/system/strs_v2.nim | 4 + 35 files changed, 4009 insertions(+), 122 deletions(-) create mode 100644 compiler/expanddefaults.nim create mode 100644 compiler/nir/ast2ir.nim create mode 100644 compiler/nir/cir.nim create mode 100644 compiler/nir/nir.nim create mode 100644 compiler/nir/nirinsts.nim create mode 100644 compiler/nir/nirlineinfos.nim create mode 100644 compiler/nir/nirslots.nim create mode 100644 compiler/nir/nirtypes.nim create mode 100644 compiler/nir/types2ir.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index dd7561264a80..4fe72929f7f8 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -936,7 +936,7 @@ type # it won't cause problems # for skModule the string literal to output for # deprecated modules. - instantiatedFrom*: PSym # for instances, the generic symbol where it came from. + instantiatedFrom*: PSym # for instances, the generic symbol where it came from. when defined(nimsuggest): allUsages*: seq[TLineInfo] @@ -2173,3 +2173,7 @@ const nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr, nkMixinStmt, nkBindStmt} + +proc isTrue*(n: PNode): bool = + n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or + n.kind == nkIntLit and n.intVal != 0 diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 756d93217c13..7d142b01d301 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -90,3 +90,8 @@ proc bitSetCard*(x: TBitSet): BiggestInt = result = 0 for it in x: result.inc int(populationCount[it]) + +proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt = + result = 0 + for j in 0..<size: + if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8)) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b2c5a5ca8fac..44d04d7633a3 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -112,11 +112,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) = proc genLiteral(p: BProc, n: PNode; result: var Rope) = genLiteral(p, n, n.typ, result) -proc bitSetToWord(s: TBitSet, size: int): BiggestUInt = - result = 0 - for j in 0..<size: - if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8)) - proc genRawSetData(cs: TBitSet, size: int; result: var Rope) = if size > 8: var res = "{\n" @@ -1561,7 +1556,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = if e[i].len == 3 and optFieldCheck in p.options: check = e[i][2] genFieldObjConstr(p, ty, useTemp, isRef, e[i][0], e[i][1], check, d, r, e.info) - + if useTemp: if d.k == locNone: d = tmp @@ -1868,8 +1863,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: unaryExpr(p, e, d, "$1.Field1") of tyCstring: - if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") + if op == mHigh: unaryExpr(p, e, d, "(#nimCStrLen($1)-1)") + else: unaryExpr(p, e, d, "#nimCStrLen($1)") of tyString: var a: TLoc = initLocExpr(p, e[1]) var x = lenExpr(p, a) @@ -1889,13 +1884,6 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") -proc makeAddr(n: PNode; idgen: IdGenerator): PNode = - if n.kind == nkHiddenAddr: - result = n - else: - result = newTree(nkHiddenAddr, n) - result.typ = makePtrType(n.typ, idgen) - proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: e[1] = makeAddr(e[1], p.module.idgen) @@ -2521,8 +2509,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) - of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") - of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") + of mGCref: + # only a magic for the old GCs + unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") + of mGCunref: + # only a magic for the old GCs + unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthSeq: genSetLengthSeq(p, e, d) of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, @@ -3217,22 +3209,6 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = else: globalError(p.config, info, "cannot create null element for: " & $t.kind) -proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = - result = 0 - for i in 1 ..< obj.len: - for j in 0 .. obj[i].len - 2: - if obj[i][j].kind == nkRange: - let x = getOrdValue(obj[i][j][0]) - let y = getOrdValue(obj[i][j][1]) - if branch >= x and branch <= y: - return i - elif getOrdValue(obj[i][j]) == branch: - return i - if obj[i].len == 1: - # else branch - return i - assert(false, "unreachable") - proc isEmptyCaseObjectBranch(n: PNode): bool = for it in n: if it.kind == nkSym and not isEmptyType(it.sym.typ): return false diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index bd0e2a58a346..47b6a1e15e7e 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -95,11 +95,11 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) else: raiseAssert "unexpected set type kind" - of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, + of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, - tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}: + tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable: discard proc specializeReset(p: BProc, a: TLoc) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index c8f68c124eba..45a234332050 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1457,7 +1457,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", + [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 2f92cb12b02a..a591ce93ba39 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -24,7 +24,7 @@ type dkResult #skResult dkConst #skConst dkOther #skType, skTemp, skLet and skForVar so far - + proc descKindFromSymKind(kind: TSymKind): TypeDescKind = case kind of skParam: dkParam @@ -33,7 +33,7 @@ proc descKindFromSymKind(kind: TSymKind): TypeDescKind = of skResult: dkResult of skConst: dkConst else: dkOther - + proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range @@ -464,7 +464,7 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr var num = 0 while i < frmt.len: if frmt[i] == c: - inc(i) + inc(i) case frmt[i] of c: res.add(c) @@ -521,7 +521,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params types.add getTypeDescWeak(m, this.typ, check, dkParam) let firstParam = if isCtor: 1 else: 2 - for i in firstParam..<t.n.len: + for i in firstParam..<t.n.len: if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams") var param = t.n[i].sym var descKind = dkParam @@ -649,7 +649,7 @@ proc mangleRecFieldName(m: BModule; field: PSym): Rope = result = rope(mangleField(m, field.name)) if result == "": internalError(m.config, field.info, "mangleRecFieldName") -proc hasCppCtor(m: BModule; typ: PType): bool = +proc hasCppCtor(m: BModule; typ: PType): bool = result = false if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType: for prc in m.g.graph.memberProcsPerType[typ.itemId]: @@ -763,7 +763,7 @@ proc getRecordFields(m: BModule; typ: PType, check: var IntSet): Rope = if isCtorGen and not isDefaultCtorGen: var ch: IntSet result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)] - + proc fillObjectFields*(m: BModule; typ: PType) = # sometimes generic objects are not consistently merged. We patch over # this fact here. @@ -771,7 +771,7 @@ proc fillObjectFields*(m: BModule; typ: PType) = discard getRecordFields(m, typ, check) proc mangleDynLibProc(sym: PSym): Rope - + proc getRecordDescAux(m: BModule; typ: PType, name, baseType: Rope, check: var IntSet, hasField:var bool): Rope = result = "" @@ -816,7 +816,7 @@ proc getRecordDesc(m: BModule; typ: PType, name: Rope, else: structOrUnion = structOrUnion(typ) var baseType: string = "" - if typ[0] != nil: + if typ[0] != nil: baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField) if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: result = structOrUnion & " " & name @@ -1128,8 +1128,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = "" # fixes bug #145: excl(check, t.id) - - + + proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope = var check = initIntSet() result = getTypeDescAux(m, typ, check, kind) @@ -1214,7 +1214,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = var name, params, rettype, superCall: string = "" var isFnConst, isOverride, isMemberVirtual: bool = false parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor) - genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) + genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) let isVirtual = sfVirtual in prc.flags or isMemberVirtual var fnConst, override: string = "" if isCtor: @@ -1224,7 +1224,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = if isFwdDecl: if isVirtual: rettype = "virtual " & rettype - if isOverride: + if isOverride: override = " override" superCall = "" else: @@ -1232,14 +1232,14 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = prc.loc.r = "$1$2(@)" % [memberOp, name] elif superCall != "": superCall = " : " & superCall - + name = "$1::$2" % [typDesc, name] result.add "N_LIB_PRIVATE " result.addf("$1$2($3, $4)$5$6$7$8", [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name, params, fnConst, override, superCall]) - + proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) = # using static is needed for inline procs var check = initIntSet() @@ -1575,10 +1575,6 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 -proc makePtrType(baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, nextTypeId idgen, baseType.owner) - addSonSkipIntLit(result, baseType, idgen) - proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym = # the wrapper is roughly like: @@ -1946,14 +1942,14 @@ proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rop else: result = genTypeInfoV1(m, t, info) -proc genTypeSection(m: BModule, n: PNode) = +proc genTypeSection(m: BModule, n: PNode) = var intSet = initIntSet() for i in 0..<n.len: if len(n[i]) == 0: continue if n[i][0].kind != nkPragmaExpr: continue for p in 0..<n[i][0].len: if (n[i][0][p].kind != nkSym): continue - if sfExportc in n[i][0][p].sym.flags: + if sfExportc in n[i][0][p].sym.flags: discard getTypeDescAux(m, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind)) if m.g.generatedHeader != nil: discard getTypeDescAux(m.g.generatedHeader, n[i][0][p].typ, intSet, descKindFromSymKind(n[i][0][p].sym.kind)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e9045b0665c5..adee8f8454b5 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -17,6 +17,8 @@ import lowerings, tables, sets, ndi, lineinfos, pathutils, transf, injectdestructors, astmsgs, modulepaths, backendpragmas +from expanddefaults import caseObjDefaultBranch + import pipelineutils when defined(nimPreviewSlimSystem): @@ -499,8 +501,8 @@ proc resetLoc(p: BProc, loc: var TLoc) = # array passed as argument decayed into pointer, bug #7332 # so we use getTypeDesc here rather than rdLoc(loc) let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc)) - if p.module.compileToCpp and isOrHasImportedCppType(typ): - if lfIndirect in loc.flags: + if p.module.compileToCpp and isOrHasImportedCppType(typ): + if lfIndirect in loc.flags: #C++ cant be just zeroed. We need to call the ctors var tmp = getTemp(p, loc.t) linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", @@ -508,7 +510,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = else: linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", [addrLoc(p.config, loc), tyDesc]) - + # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) @@ -551,7 +553,7 @@ proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, + linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.r, genCppInitializer(p.module, p, t)]) else: linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) @@ -607,8 +609,8 @@ proc assignLocalVar(p: BProc, n: PNode) = # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! let nl = if optLineDir in p.config.options: "" else: "\n" - var decl = localVarDecl(p, n) - if p.module.compileToCpp and isOrHasImportedCppType(n.typ): + var decl = localVarDecl(p, n) + if p.module.compileToCpp and isOrHasImportedCppType(n.typ): decl.add genCppInitializer(p.module, p, n.typ) decl.add ";" & nl line(p, cpsLocals, decl) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 1459bde45723..4cae9ec42bf0 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -129,10 +129,6 @@ template withBlock(labl: PSym; body: untyped) = body popBlock(c, oldLen) -proc isTrue(n: PNode): bool = - n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or - n.kind == nkIntLit and n.intVal != 0 - template forkT(body) = let lab1 = c.forkI() body diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim new file mode 100644 index 000000000000..9884332781ea --- /dev/null +++ b/compiler/expanddefaults.nim @@ -0,0 +1,131 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import lineinfos, ast, types + +proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int = + result = 0 + for i in 1 ..< obj.len: + for j in 0 .. obj[i].len - 2: + if obj[i][j].kind == nkRange: + let x = getOrdValue(obj[i][j][0]) + let y = getOrdValue(obj[i][j][1]) + if branch >= x and branch <= y: + return i + elif getOrdValue(obj[i][j]) == branch: + return i + if obj[i].len == 1: + # else branch + return i + return 1 + +template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t) + +proc expandDefault*(t: PType; info: TLineInfo): PNode + +proc expandField(s: PSym; info: TLineInfo): PNode = + result = newNodeIT(nkExprColonExpr, info, s.typ) + result.add newSymNode(s) + result.add expandDefault(s.typ, info) + +proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) = + case n.kind + of nkRecList: + for i in 0..<n.len: + expandDefaultN(n[i], info, res) + of nkRecCase: + res.add expandField(n[0].sym, info) + var branch = Zero + let constOrNil = n[0].sym.astdef + if constOrNil != nil: + branch = getOrdValue(constOrNil) + + let selectedBranch = caseObjDefaultBranch(n, branch) + let b = lastSon(n[selectedBranch]) + expandDefaultN b, info, res + of nkSym: + res.add expandField(n.sym, info) + else: + discard + +proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) = + if t[0] != nil: + expandDefaultObj(t[0], info, res) + expandDefaultN(t.n, info, res) + +proc expandDefault(t: PType; info: TLineInfo): PNode = + case t.kind + of tyInt: result = newZero(t, info, nkIntLit) + of tyInt8: result = newZero(t, info, nkInt8Lit) + of tyInt16: result = newZero(t, info, nkInt16Lit) + of tyInt32: result = newZero(t, info, nkInt32Lit) + of tyInt64: result = newZero(t, info, nkInt64Lit) + of tyUInt: result = newZero(t, info, nkUIntLit) + of tyUInt8: result = newZero(t, info, nkUInt8Lit) + of tyUInt16: result = newZero(t, info, nkUInt16Lit) + of tyUInt32: result = newZero(t, info, nkUInt32Lit) + of tyUInt64: result = newZero(t, info, nkUInt64Lit) + of tyFloat: result = newZero(t, info, nkFloatLit) + of tyFloat32: result = newZero(t, info, nkFloat32Lit) + of tyFloat64: result = newZero(t, info, nkFloat64Lit) + of tyFloat128: result = newZero(t, info, nkFloat64Lit) + of tyChar: result = newZero(t, info, nkCharLit) + of tyBool: result = newZero(t, info, nkIntLit) + of tyEnum: + # Could use low(T) here to finally fix old language quirks + result = newZero(t, info, nkIntLit) + of tyRange: + # Could use low(T) here to finally fix old language quirks + result = expandDefault(t[0], info) + of tyVoid: result = newZero(t, info, nkEmpty) + of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned: + result = expandDefault(t.lastSon, info) + of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: + if t.len > 0: + result = expandDefault(t.lastSon, info) + else: + result = newZero(t, info, nkEmpty) + of tyFromExpr: + if t.n != nil and t.n.typ != nil: + result = expandDefault(t.n.typ, info) + else: + result = newZero(t, info, nkEmpty) + of tyArray: + result = newZero(t, info, nkBracket) + let n = toInt64(lengthOrd(nil, t)) + for i in 0..<n: + result.add expandDefault(t[1], info) + of tyPtr, tyRef, tyProc, tyPointer, tyCstring: + result = newZero(t, info, nkNilLit) + of tyVar, tyLent: + let e = t.lastSon + if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + # skip the modifier, `var openArray` is a (ptr, len) pair too: + result = expandDefault(e, info) + else: + result = newZero(t.lastSon, info, nkNilLit) + of tySet: + result = newZero(t, info, nkCurly) + of tyObject: + result = newNodeIT(nkObjConstr, info, t) + result.add newNodeIT(nkType, info, t) + expandDefaultObj(t, info, result) + of tyTuple: + result = newZero(t, info, nkTupleConstr) + for it in t: + result.add expandDefault(it, info) + of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray: + result = newZero(t, info, nkBracket) + of tyString: + result = newZero(t, info, nkStrLit) + of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc, + tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, + tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: + result = newZero(t, info, nkEmpty) # bug indicator diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index a5513105113a..edbaeb240ea6 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -13,6 +13,8 @@ type vals: seq[T] # indexed by LitId keys: seq[LitId] # indexed by hash(val) +proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[]) + proc nextTry(h, maxHash: Hash): Hash {.inline.} = result = (h + 1) and maxHash diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index bb8c6a5b98c8..63fe0369c8f7 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -150,4 +150,13 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym = globalError(g.config, info, "can't find magic equals operator for type kind " & $t.kind) +proc makePtrType*(baseType: PType; idgen: IdGenerator): PType = + result = newType(tyPtr, nextTypeId idgen, baseType.owner) + addSonSkipIntLit(result, baseType, idgen) +proc makeAddr*(n: PNode; idgen: IdGenerator): PNode = + if n.kind == nkHiddenAddr: + result = n + else: + result = newTree(nkHiddenAddr, n) + result.typ = makePtrType(n.typ, idgen) diff --git a/compiler/main.nim b/compiler/main.nim index 7118a253c4d8..e5a5be56c1a4 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,6 +22,8 @@ import modules, modulegraphs, lineinfos, pathutils, vmprofiler +# ensure NIR compiles: +import nir / nir when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -173,13 +175,14 @@ proc commandCompileToJS(graph: ModuleGraph) = if optGenScript in conf.globalOptions: writeDepsFile(graph) -proc commandInteractive(graph: ModuleGraph) = +proc commandInteractive(graph: ModuleGraph; useNir: bool) = graph.config.setErrorMaxHighMaybe initDefines(graph.config.symbols) - defineSymbol(graph.config.symbols, "nimscript") + if not useNir: + defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - setPipeLinePass(graph, InterpreterPass) + setPipeLinePass(graph, if useNir: NirReplPass else: InterpreterPass) compilePipelineSystemModule(graph) if graph.config.commandArgs.len > 0: discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) @@ -407,7 +410,7 @@ proc mainCommand*(graph: ModuleGraph) = wantMainModule(conf) commandView(graph) #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") - of cmdInteractive: commandInteractive(graph) + of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir")) of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 4dadb45b6ccf..f6abb0a60895 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -65,6 +65,7 @@ type CgenPass EvalPass InterpreterPass + NirReplPass GenDependPass Docgen2TexPass Docgen2JsonPass @@ -99,6 +100,7 @@ type cache*: IdentCache vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will # be clarified in later compiler implementations. + repl*: RootRef # REPL state is shared project-wise. doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] diff --git a/compiler/nim.nim b/compiler/nim.nim index d0aa888c4146..023a76ff9b12 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -116,7 +116,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.backend = backendC if conf.selectedGC == gcUnselected: - if conf.backend in {backendC, backendCpp, backendObjc}: + if conf.backend in {backendC, backendCpp, backendObjc} or + (conf.cmd == cmdInteractive and isDefined(conf, "nir")): initOrcDefines(conf) mainCommand(graph) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim new file mode 100644 index 000000000000..f06bb3ae8103 --- /dev/null +++ b/compiler/nir/ast2ir.nim @@ -0,0 +1,2189 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import std / [assertions, tables, sets] +import ".." / [ast, astalgo, types, options, lineinfos, msgs, magicsys, + modulegraphs, guards, renderer, transf, bitsets, trees, nimsets, + expanddefaults] +from ".." / lowerings import lowerSwap, lowerTupleUnpacking +from ".." / pathutils import customPath +import .. / ic / bitabs + +import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir + +type + ModuleCon* = ref object + strings*: BiTable[string] + integers*: BiTable[int64] + man*: LineInfoManager + types*: TypesCon + slotGenerator: ref int + module*: PSym + graph*: ModuleGraph + nativeIntId, nativeUIntId: TypeId + idgen: IdGenerator + pendingProcs: Table[ItemId, PSym] # procs we still need to generate code for + + ProcCon* = object + config: ConfigRef + lastFileKey: FileIndex + lastFileVal: LitId + labelGen: int + exitLabel: LabelId + code*: Tree + blocks: seq[(PSym, LabelId)] + sm: SlotManager + locGen: int + m: ModuleCon + prc: PSym + +proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym): ModuleCon = + result = ModuleCon(graph: graph, types: initTypesCon(config), slotGenerator: new(int), + idgen: idgen, module: module) + case config.target.intSize + of 2: + result.nativeIntId = Int16Id + result.nativeUIntId = UInt16Id + of 4: + result.nativeIntId = Int32Id + result.nativeUIntId = UInt16Id + else: + result.nativeIntId = Int64Id + result.nativeUIntId = UInt16Id + +proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon = + ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config) + +proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = + var val: LitId + if c.lastFileKey == i.fileIndex: + val = c.lastFileVal + else: + val = c.m.strings.getOrIncl(toFullPath(c.config, i.fileIndex)) + # remember the entry: + c.lastFileKey = i.fileIndex + c.lastFileVal = val + result = pack(c.m.man, val, int32 i.line, int32 i.col) + +proc bestEffort(c: ProcCon): TLineInfo = + if c.prc != nil: + c.prc.info + else: + c.m.module.info + +proc popBlock(c: var ProcCon; oldLen: int) = + c.blocks.setLen(oldLen) + +template withBlock(labl: PSym; info: PackedLineInfo; asmLabl: LabelId; body: untyped) {.dirty.} = + var oldLen {.gensym.} = c.blocks.len + c.blocks.add (labl, asmLabl) + body + popBlock(c, oldLen) + +type + GenFlag = enum + gfAddrOf # load the address of the expression + gfToOutParam # the expression is passed to an `out` parameter + GenFlags = set[GenFlag] + +proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) + +proc genScope(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = + openScope c.sm + gen c, n, d, flags + closeScope c.sm + +proc freeTemp(c: var ProcCon; tmp: Value) = + let s = extractTemp(tmp) + if s != SymId(-1): + freeTemp(c.sm, s) + +proc getTemp(c: var ProcCon; n: PNode): Value = + let info = toLineInfo(c, n.info) + let t = typeToIr(c.m.types, n.typ) + let tmp = allocTemp(c.sm, t) + c.code.addSummon info, tmp, t + result = localToValue(info, tmp) + +template withTemp(tmp, n, body: untyped) {.dirty.} = + var tmp = getTemp(c, n) + body + c.freeTemp(tmp) + +proc gen(c: var ProcCon; n: PNode; flags: GenFlags = {}) = + var tmp = default(Value) + gen(c, n, tmp, flags) + freeTemp c, tmp + +proc genScope(c: var ProcCon; n: PNode; flags: GenFlags = {}) = + openScope c.sm + gen c, n, flags + closeScope c.sm + +proc genx(c: var ProcCon; n: PNode; flags: GenFlags = {}): Value = + result = default(Value) + gen(c, n, result, flags) + assert Tree(result).len > 0, $n + +proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} = + when false: + if n.typ.isNil or n.typ.kind == tyVoid: + let s = extractTemp(d) + if s != SymId(-1): + freeLoc(c.sm, s) + +proc isNotOpr(n: PNode): bool = + n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot + +proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) = + c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab + +type + JmpKind = enum opcFJmp, opcTJmp + +proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId = + result = newLabel(c.labelGen) + let info = toLineInfo(c, n.info) + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree Tree(v) + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, jk == opcTJmp) + c.code.gotoLabel info, Goto, result + +proc patch(c: var ProcCon; n: PNode; L: LabelId) = + addLabel c.code, toLineInfo(c, n.info), Label, L + +proc genWhile(c: var ProcCon; n: PNode) = + # lab1: + # cond, tmp + # fjmp tmp, lab2 + # body + # jmp lab1 + # lab2: + let info = toLineInfo(c, n.info) + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + withBlock(nil, info, lab1): + if isTrue(n[0]): + c.gen(n[1]) + c.jmpBack(n, lab1) + elif isNotOpr(n[0]): + var tmp = c.genx(n[0][1]) + let lab2 = c.xjmp(n, opcTJmp, tmp) + c.freeTemp(tmp) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(n, lab2) + else: + var tmp = c.genx(n[0]) + let lab2 = c.xjmp(n, opcFJmp, tmp) + c.freeTemp(tmp) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(n, lab2) + +proc genBlock(c: var ProcCon; n: PNode; d: var Value) = + openScope c.sm + let info = toLineInfo(c, n.info) + let lab1 = newLabel(c.labelGen) + + withBlock(n[0].sym, info, lab1): + c.gen(n[1], d) + + c.code.addLabel(info, Label, lab1) + closeScope c.sm + c.clearDest(n, d) + +proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) = + c.code.addLabel(toLineInfo(c, n.info), Goto, L) + +proc genBreak(c: var ProcCon; n: PNode) = + if n[0].kind == nkSym: + for i in countdown(c.blocks.len-1, 0): + if c.blocks[i][0] == n[0].sym: + c.jumpTo n, c.blocks[i][1] + return + localError(c.config, n.info, "NIR problem: cannot find 'break' target") + else: + c.jumpTo n, c.blocks[c.blocks.high][1] + +proc genIf(c: var ProcCon; n: PNode; d: var Value) = + # if (!expr1) goto lab1; + # thenPart + # goto LEnd + # lab1: + # if (!expr2) goto lab2; + # thenPart2 + # goto LEnd + # lab2: + # elsePart + # Lend: + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + var ending = newLabel(c.labelGen) + for i in 0..<n.len: + var it = n[i] + if it.len == 2: + let info = toLineInfo(c, it[0].info) + withTemp(tmp, it[0]): + var elsePos: LabelId + if isNotOpr(it[0]): + c.gen(it[0][1], tmp) + elsePos = c.xjmp(it[0][1], opcTJmp, tmp) # if true + else: + c.gen(it[0], tmp) + elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false + c.clearDest(n, d) + if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `d` + c.genScope(it[1]) + else: + c.genScope(it[1], d) # then part + if i < n.len-1: + c.jumpTo it[1], ending + c.patch(it, elsePos) + else: + c.clearDest(n, d) + if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `d` + c.genScope(it[0]) + else: + c.genScope(it[0], d) + c.patch(n, ending) + c.clearDest(n, d) + +proc tempToDest(c: var ProcCon; n: PNode; d: var Value; tmp: Value) = + if isEmpty(d): + d = tmp + else: + let info = toLineInfo(c, n.info) + build c.code, info, Asgn: + c.code.addTyped info, typeToIr(c.m.types, n.typ) + c.code.copyTree d + c.code.copyTree tmp + freeTemp(c, tmp) + +proc genAndOr(c: var ProcCon; n: PNode; opc: JmpKind; d: var Value) = + # asgn d, a + # tjmp|fjmp lab1 + # asgn d, b + # lab1: + var tmp = getTemp(c, n) + c.gen(n[1], tmp) + let lab1 = c.xjmp(n, opc, tmp) + c.gen(n[2], tmp) + c.patch(n, lab1) + tempToDest c, n, d, tmp + +proc unused(c: var ProcCon; n: PNode; x: Value) {.inline.} = + if hasValue(x): + #debug(n) + localError(c.config, n.info, "not unused") + +proc caseValue(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + build c.code, info, SelectValue: + let x = genx(c, n) + c.code.copyTree x + freeTemp(c, x) + +proc caseRange(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + build c.code, info, SelectRange: + let x = genx(c, n[0]) + let y = genx(c, n[1]) + c.code.copyTree x + c.code.copyTree y + freeTemp(c, y) + freeTemp(c, x) + +proc genCase(c: var ProcCon; n: PNode; d: var Value) = + if not isEmptyType(n.typ): + if isEmpty(d): d = getTemp(c, n) + else: + unused(c, n, d) + var sections = newSeqOfCap[LabelId](n.len-1) + let ending = newLabel(c.labelGen) + let info = toLineInfo(c, n.info) + withTemp(tmp, n[0]): + build c.code, info, Select: + c.code.addTyped info, typeToIr(c.m.types, n[0].typ) + c.gen(n[0], tmp) + for i in 1..<n.len: + let section = newLabel(c.labelGen) + sections.add section + let it = n[i] + let itinfo = toLineInfo(c, it.info) + build c.code, itinfo, SelectPair: + build c.code, itinfo, SelectList: + for j in 0..<it.len-1: + if it[j].kind == nkRange: + caseRange c, it[j] + else: + caseValue c, it[j] + c.code.addLabel itinfo, Goto, section + for i in 1..<n.len: + let it = n[i] + let itinfo = toLineInfo(c, it.info) + c.code.addLabel itinfo, Label, sections[i-1] + c.gen it.lastSon + if i != n.len-1: + c.code.addLabel itinfo, Goto, ending + c.code.addLabel info, Label, ending + +proc rawCall(c: var ProcCon; info: PackedLineInfo; opc: Opcode; t: TypeId; args: var openArray[Value]) = + build c.code, info, opc: + c.code.addTyped info, t + if opc in {CheckedCall, CheckedIndirectCall}: + c.code.addLabel info, CheckedGoto, c.exitLabel + for a in mitems(args): + c.code.copyTree a + freeTemp c, a + +proc canRaiseDisp(c: ProcCon; n: PNode): bool = + # we assume things like sysFatal cannot raise themselves + if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: + result = false + elif optPanics in c.config.globalOptions or + (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and + sfSystemRaisesDefect notin n.sym.flags): + # we know we can be strict: + result = canRaise(n) + else: + # we have to be *very* conservative: + result = canRaiseConservative(n) + +proc genCall(c: var ProcCon; n: PNode; d: var Value) = + let canRaise = canRaiseDisp(c, n[0]) + + let opc = if n[0].kind == nkSym and n[0].sym.kind in routineKinds: + (if canRaise: CheckedCall else: Call) + else: + (if canRaise: CheckedIndirectCall else: IndirectCall) + let info = toLineInfo(c, n.info) + + # In the IR we cannot nest calls. Thus we use two passes: + var args: seq[Value] = @[] + var t = n[0].typ + if t != nil: t = t.skipTypes(abstractInst) + args.add genx(c, n[0]) + for i in 1..<n.len: + if t != nil and i < t.len: + if isCompileTimeOnly(t[i]): discard + elif isOutParam(t[i]): args.add genx(c, n[i], {gfToOutParam}) + else: args.add genx(c, n[i]) + else: + args.add genx(c, n[i]) + + let tb = typeToIr(c.m.types, n.typ) + if not isEmptyType(n.typ): + if isEmpty(d): d = getTemp(c, n) + # XXX Handle problematic aliasing here: `a = f_canRaise(a)`. + build c.code, info, Asgn: + c.code.addTyped info, tb + c.code.copyTree d + rawCall c, info, opc, tb, args + else: + rawCall c, info, opc, tb, args + +proc genRaise(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + let tb = typeToIr(c.m.types, n[0].typ) + + let d = genx(c, n[0]) + build c.code, info, SetExc: + c.code.addTyped info, tb + c.code.copyTree d + c.freeTemp(d) + c.code.addLabel info, Goto, c.exitLabel + +proc genReturn(c: var ProcCon; n: PNode) = + if n[0].kind != nkEmpty: + gen(c, n[0]) + # XXX Block leave actions? + let info = toLineInfo(c, n.info) + c.code.addLabel info, Goto, c.exitLabel + +proc genTry(c: var ProcCon; n: PNode; d: var Value) = + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + var endings: seq[LabelId] = @[] + let ehPos = newLabel(c.labelGen) + let oldExitLab = c.exitLabel + c.exitLabel = ehPos + if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `d` + c.gen(n[0]) + else: + c.gen(n[0], d) + c.clearDest(n, d) + + # Add a jump past the exception handling code + let jumpToFinally = newLabel(c.labelGen) + c.jumpTo n, jumpToFinally + # This signals where the body ends and where the exception handling begins + c.patch(n, ehPos) + c.exitLabel = oldExitLab + for i in 1..<n.len: + let it = n[i] + if it.kind != nkFinally: + # first opcExcept contains the end label of the 'except' block: + let endExcept = newLabel(c.labelGen) + for j in 0..<it.len - 1: + assert(it[j].kind == nkType) + let typ = it[j].typ.skipTypes(abstractPtrs-{tyTypeDesc}) + let itinfo = toLineInfo(c, it[j].info) + build c.code, itinfo, TestExc: + c.code.addTyped itinfo, typeToIr(c.m.types, typ) + if it.len == 1: + let itinfo = toLineInfo(c, it.info) + build c.code, itinfo, TestExc: + c.code.addTyped itinfo, VoidId + let body = it.lastSon + if isEmptyType(body.typ): # maybe noreturn call, don't touch `d` + c.gen(body) + else: + c.gen(body, d) + c.clearDest(n, d) + if i < n.len: + endings.add newLabel(c.labelGen) + c.patch(it, endExcept) + let fin = lastSon(n) + # we always generate an 'opcFinally' as that pops the safepoint + # from the stack if no exception is raised in the body. + c.patch(fin, jumpToFinally) + #c.gABx(fin, opcFinally, 0, 0) + for endPos in endings: c.patch(n, endPos) + if fin.kind == nkFinally: + c.gen(fin[0]) + c.clearDest(n, d) + #c.gABx(fin, opcFinallyEnd, 0, 0) + +template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar +proc isGlobal(n: PNode): bool = n.kind == nkSym and isGlobal(n.sym) + +proc genField(c: var ProcCon; n: PNode; d: var Value) = + var pos: int + if n.kind != nkSym or n.sym.kind != skField: + localError(c.config, n.info, "no field symbol") + pos = 0 + else: + pos = n.sym.position + d.addImmediateVal toLineInfo(c, n.info), pos + +proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = + if arr.skipTypes(abstractInst).kind == tyArray and + (let x = firstOrd(c.config, arr); x != Zero): + let info = toLineInfo(c, n.info) + buildTyped d, info, Sub, c.m.nativeIntId: + c.gen(n, d) + d.addImmediateVal toLineInfo(c, n.info), toInt(x) + else: + c.gen(n, d) + +proc genNew(c: var ProcCon; n: PNode; needsInit: bool) = + # If in doubt, always follow the blueprint of the C code generator for `mm:orc`. + let refType = n[1].typ.skipTypes(abstractInstOwned) + assert refType.kind == tyRef + let baseType = refType.lastSon + + let info = toLineInfo(c, n.info) + let codegenProc = magicsys.getCompilerProc(c.m.graph, + if needsInit: "nimNewObj" else: "nimNewObjUninit") + let x = genx(c, n[1]) + let refTypeIr = typeToIr(c.m.types, refType) + buildTyped c.code, info, Asgn, refTypeIr: + copyTree c.code, x + buildTyped c.code, info, Cast, refTypeIr: + buildTyped c.code, info, Call, VoidPtrId: + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + c.code.addImmediateVal info, int(getSize(c.config, baseType)) + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + +proc genNewSeqOfCap(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let seqtype = skipTypes(n.typ, abstractVarRange) + let baseType = seqtype.lastSon + var a = c.genx(n[1]) + if isEmpty(d): d = getTemp(c, n) + # $1.len = 0 + buildTyped c.code, info, Asgn, c.m.nativeIntId: + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, d + c.code.addImmediateVal info, 0 + c.code.addImmediateVal info, 0 + # $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) + let payloadPtr = seqPayloadPtrType(c.m.types, seqtype) + buildTyped c.code, info, Asgn, payloadPtr: + # $1.p + buildTyped c.code, info, FieldAt, payloadPtr: + copyTree c.code, d + c.code.addImmediateVal info, 1 + # ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3)) + buildTyped c.code, info, Cast, payloadPtr: + buildTyped c.code, info, Call, VoidPtrId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayloadUninit") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, a + c.code.addImmediateVal info, int(getSize(c.config, baseType)) + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + freeTemp c, a + +proc genNewSeq(c: var ProcCon; n: PNode) = + let info = toLineInfo(c, n.info) + let seqtype = skipTypes(n[1].typ, abstractVarRange) + let baseType = seqtype.lastSon + var d = c.genx(n[1]) + var b = c.genx(n[2]) + + # $1.len = $2 + buildTyped c.code, info, Asgn, c.m.nativeIntId: + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, d + c.code.addImmediateVal info, 0 + copyTree c.code, b + c.code.addImmediateVal info, 0 + + # $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) + let payloadPtr = seqPayloadPtrType(c.m.types, seqtype) + buildTyped c.code, info, Asgn, payloadPtr: + # $1.p + buildTyped c.code, info, FieldAt, payloadPtr: + copyTree c.code, d + c.code.addImmediateVal info, 1 + # ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3)) + buildTyped c.code, info, Cast, payloadPtr: + buildTyped c.code, info, Call, VoidPtrId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "newSeqPayload") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, b + c.code.addImmediateVal info, int(getSize(c.config, baseType)) + c.code.addImmediateVal info, int(getAlign(c.config, baseType)) + freeTemp c, b + freeTemp c, d + +template intoDest*(d: var Value; info: PackedLineInfo; typ: TypeId; body: untyped) = + if typ == VoidId: + body(c.code) + elif isEmpty(d): + body(Tree(d)) + else: + buildTyped c.code, info, Asgn, typ: + copyTree c.code, d + body(c.code) + +proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let tmp2 = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, opc, t: + copyTree target, tmp + copyTree target, tmp2 + intoDest d, info, t, body + c.freeTemp(tmp) + c.freeTemp(tmp2) + +proc genCmpOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let tmp2 = c.genx(n[2]) + let t = typeToIr(c.m.types, n[1].typ) + template body(target) = + buildTyped target, info, opc, t: + copyTree target, tmp + copyTree target, tmp2 + intoDest d, info, Bool8Id, body + c.freeTemp(tmp) + c.freeTemp(tmp2) + +proc genUnaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, opc, t: + copyTree target, tmp + intoDest d, info, t, body + c.freeTemp(tmp) + +proc genIncDec(c: var ProcCon; n: PNode; opc: Opcode) = + let info = toLineInfo(c, n.info) + let t = typeToIr(c.m.types, skipTypes(n[1].typ, abstractVar)) + + let d = c.genx(n[1]) + let tmp = c.genx(n[2]) + # we produce code like: i = i + 1 + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, opc, t: + copyTree c.code, d + copyTree c.code, tmp + c.freeTemp(tmp) + #c.genNarrow(n[1], d) + c.freeTemp(d) + +proc genArrayLen(c: var ProcCon; n: PNode; d: var Value) = + #echo c.m.graph.config $ n.info, " ", n + let info = toLineInfo(c, n.info) + var a = n[1] + #if a.kind == nkHiddenAddr: a = a[0] + var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) + case typ.kind + of tyOpenArray, tyVarargs: + let xa = c.genx(a) + template body(target) = + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, xa + target.addImmediateVal info, 1 # (p, len)-pair so len is at index 1 + intoDest d, info, c.m.nativeIntId, body + + of tyCstring: + let xa = c.genx(a) + if isEmpty(d): d = getTemp(c, n) + buildTyped c.code, info, Call, c.m.nativeIntId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCStrLen") + assert codegenProc != nil + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, xa + + of tyString, tySequence: + let xa = c.genx(a) + + if typ.kind == tySequence: + # we go through a temporary here because people write bullshit code. + if isEmpty(d): d = getTemp(c, n) + + template body(target) = + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, xa + target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + intoDest d, info, c.m.nativeIntId, body + + of tyArray: + template body(target) = + target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) + intoDest d, info, c.m.nativeIntId, body + else: internalError(c.config, n.info, "genArrayLen()") + +proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, Sub, t: + # Little hack: This works because we know that `0.0` is all 0 bits: + target.addIntVal(c.m.integers, info, t, 0) + copyTree target, tmp + intoDest d, info, t, body + c.freeTemp(tmp) + +proc genHigh(c: var ProcCon; n: PNode; d: var Value) = + let subOpr = createMagic(c.m.graph, c.m.idgen, "-", mSubI) + let lenOpr = createMagic(c.m.graph, c.m.idgen, "len", mLengthOpenArray) + let asLenExpr = subOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(1)) + c.gen asLenExpr, d + +proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) = + let info = toLineInfo(c, n.info) + let xa = c.genx(n[1]) + let xb = c.genx(n[2]) + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree target, theProc + copyTree target, xa + copyTree target, xb + + intoDest d, info, t, body + c.freeTemp xb + c.freeTemp xa + +proc genUnaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string; argAt = 1) = + let info = toLineInfo(c, n.info) + let xa = c.genx(n[argAt]) + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, compilerProc) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree target, theProc + copyTree target, xa + + intoDest d, info, t, body + c.freeTemp xa + +proc genEnumToStr(c: var ProcCon; n: PNode; d: var Value) = + let t = n[1].typ.skipTypes(abstractInst+{tyRange}) + let toStrProc = getToStringProc(c.m.graph, t) + # XXX need to modify this logic for IC. + var nb = copyTree(n) + nb[0] = newSymNode(toStrProc) + gen(c, nb, d) + +proc genOf(c: var ProcCon; n: PNode; d: var Value) = + genUnaryOp c, n, d, TestOf + +template sizeOfLikeMsg(name): string = + "'" & name & "' requires '.importc' types to be '.completeStruct'" + +proc genIsNil(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[1]) + let t = typeToIr(c.m.types, n[1].typ) + template body(target) = + buildTyped target, info, Eq, t: + copyTree target, tmp + addNilVal target, info, t + intoDest d, info, Bool8Id, body + c.freeTemp(tmp) + +proc fewCmps(conf: ConfigRef; s: PNode): bool = + # this function estimates whether it is better to emit code + # for constructing the set or generating a bunch of comparisons directly + if s.kind != nkCurly: + result = false + elif (getSize(conf, s.typ) <= conf.target.intSize) and (nfAllConst in s.flags): + result = false # it is better to emit the set generation code + elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: + result = true # better not emit the set if int is basetype! + else: + result = s.len <= 8 # 8 seems to be a good value + +proc genInBitset(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + + let t = bitsetBasetype(c.m.types, n[1].typ) + let setType = typeToIr(c.m.types, n[1].typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + let expansion = if t == UInt64Id: UInt64Id else: c.m.nativeUIntId + # "(($1 &(1U<<((NU)($2)&7U)))!=0)" - or - + # "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)" + + template body(target) = + buildTyped target, info, BoolNot, Bool8Id: + buildTyped target, info, Eq, t: + buildTyped target, info, BitAnd, t: + if c.m.types.g[setType].kind != ArrayTy: + copyTree target, a + else: + buildTyped target, info, ArrayAt, t: + copyTree target, a + buildTyped target, info, BitShr, t: + buildTyped target, info, Cast, expansion: + copyTree target, b + addIntVal target, c.m.integers, info, expansion, 3 + + buildTyped target, info, BitShl, t: + addIntVal target, c.m.integers, info, t, 1 + buildTyped target, info, BitAnd, t: + buildTyped target, info, Cast, expansion: + copyTree target, b + addIntVal target, c.m.integers, info, expansion, mask + addIntVal target, c.m.integers, info, t, 0 + intoDest d, info, t, body + + c.freeTemp(b) + c.freeTemp(a) + +proc genInSet(c: var ProcCon; n: PNode; d: var Value) = + let g {.cursor.} = c.m.graph + if n[1].kind == nkCurly and fewCmps(g.config, n[1]): + # a set constructor but not a constant set: + # do not emit the set, but generate a bunch of comparisons; and if we do + # so, we skip the unnecessary range check: This is a semantical extension + # that code now relies on. :-/ XXX + let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0] + else: n[2] + let curly = n[1] + var ex: PNode = nil + for it in curly: + var test: PNode + if it.kind == nkRange: + test = newTree(nkCall, g.operators.opAnd.newSymNode, + newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem + newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1]) + ) + else: + test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it) + test.typ = getSysType(g, it.info, tyBool) + + if ex == nil: ex = test + else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test) + + if ex == nil: + let info = toLineInfo(c, n.info) + template body(target) = + boolVal target, info, false + intoDest d, info, Bool8Id, body + else: + gen c, ex, d + else: + genInBitset c, n, d + +proc genCard(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, Call, t: + if c.m.types.g[setType].kind == ArrayTy: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, a + c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) + elif t == UInt64Id: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, a + else: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, Cast, UInt32Id: + copyTree c.code, a + freeTemp c, a + +proc genEqSet(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + if isEmpty(d): d = getTemp(c, n) + + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, Eq, t: + buildTyped c.code, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, a + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, b + c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + + else: + template body(target) = + buildTyped target, info, Eq, setType: + copyTree target, a + copyTree target, b + intoDest d, info, Bool8Id, body + + freeTemp c, b + freeTemp c, a + +proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) = + let tmp = allocTemp(c.sm, c.m.nativeIntId) + c.code.addSummon info, tmp, c.m.nativeIntId + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmp + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, first + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + result = (tmp, lab1, newLabel(c.labelGen)) + + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Lt, c.m.nativeIntId: + c.code.addSymUse info, tmp + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, last + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, result[2] + +proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) = + let tmp = allocTemp(c.sm, c.m.nativeIntId) + c.code.addSummon info, tmp, c.m.nativeIntId + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmp + copyTree c.code, first + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + result = (tmp, lab1, newLabel(c.labelGen)) + + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Le, c.m.nativeIntId: + c.code.addSymUse info, tmp + copyTree c.code, last + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, result[2] + +proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) = + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, s + buildTyped c.code, info, Add, c.m.nativeIntId: + c.code.addSymUse info, s + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 1 + c.code.addLabel info, GotoLoop, back + c.code.addLabel info, Label, exit + freeTemp(c.sm, s) + +proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + let elemType = bitsetBasetype(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + # "for ($1 = 0; $1 < $2; $1++):" + # " $3 = (($4[$1] & ~ $5[$1]) == 0)" + # " if (!$3) break;" + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) + buildTyped c.code, info, Asgn, Bool8Id: + copyTree c.code, d + buildTyped c.code, info, Eq, elemType: + buildTyped c.code, info, BitAnd, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, a + c.code.addSymUse info, idx + buildTyped c.code, info, BitNot, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, elemType, 0 + + # if !$3: break + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree d + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, endLabel + + endLoop(c, info, idx, backLabel, endLabel) + else: + # "(($1 & ~ $2)==0)" + template body(target) = + buildTyped target, info, Eq, setType: + buildTyped target, info, BitAnd, setType: + copyTree target, a + buildTyped target, info, BitNot, setType: + copyTree target, b + target.addIntVal c.m.integers, info, setType, 0 + + intoDest d, info, Bool8Id, body + + freeTemp c, b + freeTemp c, a + +proc genLtSet(c: var ProcCon; n: PNode; d: var Value) = + localError(c.m.graph.config, n.info, "`<` for sets not implemented") + +proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + let elemType = bitsetBasetype(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + # "for ($1 = 0; $1 < $2; $1++):" + # " $3 = (($4[$1] & ~ $5[$1]) == 0)" + # " if (!$3) break;" + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) + buildTyped c.code, info, Asgn, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, d + c.code.addSymUse info, idx + buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, a + c.code.addSymUse info, idx + if m == mMinusSet: + buildTyped c.code, info, BitNot, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + else: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + + endLoop(c, info, idx, backLabel, endLabel) + else: + # "(($1 & ~ $2)==0)" + template body(target) = + buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType: + copyTree target, a + if m == mMinusSet: + buildTyped target, info, BitNot, setType: + copyTree target, b + else: + copyTree target, b + + intoDest d, info, setType, body + + freeTemp c, b + freeTemp c, a + +proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + + let setType = typeToIr(c.m.types, n[1].typ) + + let t = bitsetBasetype(c.m.types, n[1].typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + + buildTyped c.code, info, Asgn, setType: + if c.m.types.g[setType].kind == ArrayTy: + if m == mIncl: + # $1[(NU)($2)>>3] |=(1U<<($2&7U)) + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, 7 + else: + # $1[(NU)($2)>>3] &= ~(1U<<($2&7U)) + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitAnd, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, 7 + + else: + copyTree c.code, a + if m == mIncl: + # $1 |= ((NU8)1)<<(($2) & 7) + buildTyped c.code, info, BitOr, setType: + copyTree c.code, a + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, mask + else: + # $1 &= ~(((NU8)1) << (($2) & 7)) + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, a + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, mask + freeTemp c, b + freeTemp c, a + +proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = + # example: { a..b, c, d, e, f..g } + # we have to emit an expression of the form: + # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); + # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); + let info = toLineInfo(c, n.info) + let setType = typeToIr(c.m.types, n.typ) + let size = int(getSize(c.config, n.typ)) + let t = bitsetBasetype(c.m.types, n.typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + + if isEmpty(d): d = getTemp(c, n) + if c.m.types.g[setType].kind != ArrayTy: + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + c.code.addIntVal c.m.integers, info, t, 0 + + for it in n: + if it.kind == nkRange: + let a = genx(c, it[0]) + let b = genx(c, it[1]) + let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, d + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, t, mask + + endLoop(c, info, idx, backLabel, endLabel) + freeTemp c, b + freeTemp c, a + + else: + let a = genx(c, it) + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, d + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, a + c.code.addIntVal c.m.integers, info, t, mask + freeTemp c, a + + else: + # init loop: + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size) + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + c.code.addIntVal c.m.integers, info, t, 0 + endLoop(c, info, idx, backLabel, endLabel) + + # incl elements: + for it in n: + if it.kind == nkRange: + let a = genx(c, it[0]) + let b = genx(c, it[1]) + let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) + + buildTyped c.code, info, Asgn, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + c.code.addSymUse info, idx + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + c.code.addSymUse info, idx + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, t, 7 + + endLoop(c, info, idx, backLabel, endLabel) + freeTemp c, b + freeTemp c, a + + else: + let a = genx(c, it) + # $1[(NU)($2)>>3] |=(1U<<($2&7U)) + buildTyped c.code, info, Asgn, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, a + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, a + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, a + c.code.addIntVal c.m.integers, info, t, 7 + freeTemp c, a + +proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = + if isDeepConstExpr(n): + let info = toLineInfo(c, n.info) + let setType = typeToIr(c.m.types, n.typ) + let size = int(getSize(c.config, n.typ)) + let cs = toBitSet(c.config, n) + + if c.m.types.g[setType].kind != ArrayTy: + template body(target) = + target.addIntVal c.m.integers, info, setType, cast[BiggestInt](bitSetToWord(cs, size)) + intoDest d, info, setType, body + else: + let t = bitsetBasetype(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayConstr, setType: + for i in 0..high(cs): + target.addIntVal c.m.integers, info, t, int64 cs[i] + intoDest d, info, setType, body + else: + genSetConstrDyn c, n, d + +proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + # <Nim code> + # s = "Hello " & name & ", how do you feel?" & 'z' + # + # <generated code> + # { + # string tmp0; + # ... + # tmp0 = rawNewString(6 + 17 + 1 + s2->len); + # // we cannot generate s = rawNewString(...) here, because + # // ``s`` may be used on the right side of the expression + # appendString(tmp0, strlit_1); + # appendString(tmp0, name); + # appendString(tmp0, strlit_2); + # appendChar(tmp0, 'z'); + # asgn(s, tmp0); + # } + var args: seq[Value] = @[] + var precomputedLen = 0 + for i in 1 ..< n.len: + let it = n[i] + if skipTypes(it.typ, abstractVarRange).kind == tyChar: + inc precomputedLen + elif it.kind in {nkStrLit..nkTripleStrLit}: + inc precomputedLen, it.strVal.len + args.add genx(c, it) + + # generate length computation: + var tmpLen = allocTemp(c.sm, c.m.nativeIntId) + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, precomputedLen + for a in mitems(args): + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + buildTyped c.code, info, CheckedAdd, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, a + c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + var tmpStr = getTemp(c, n) + # ^ because of aliasing, we always go through a temporary + let t = typeToIr(c.m.types, n.typ) + buildTyped c.code, info, Asgn, t: + copyTree c.code, tmpStr + buildTyped c.code, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString") + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + c.code.addSymUse info, tmpLen + freeTemp c.sm, tmpLen + + for i in 1 ..< n.len: + let it = n[i] + let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar + buildTyped c.code, info, Call, VoidId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, + (if isChar: "appendChar" else: "appendString")) + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, t): + copyTree c.code, tmpStr + copyTree c.code, args[i-1] + freeTemp c, args[i-1] + + if isEmpty(d): + d = tmpStr + else: + # XXX Test that this does not cause memory leaks! + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + copyTree c.code, tmpStr + +proc genDefault(c: var ProcCon; n: PNode; d: var Value) = + let m = expandDefault(n.typ, n.info) + gen c, m, d + +proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = + case m + of mAnd: c.genAndOr(n, opcFJmp, d) + of mOr: c.genAndOr(n, opcTJmp, d) + of mPred, mSubI: c.genBinaryOp(n, d, CheckedSub) + of mSucc, mAddI: c.genBinaryOp(n, d, CheckedAdd) + of mInc: + unused(c, n, d) + c.genIncDec(n, CheckedAdd) + of mDec: + unused(c, n, d) + c.genIncDec(n, CheckedSub) + of mOrd, mChr, mArrToSeq, mUnown: + c.gen(n[1], d) + of generatedMagics: + genCall(c, n, d) + of mNew, mNewFinalize: + unused(c, n, d) + c.genNew(n, needsInit = true) + of mNewSeq: + unused(c, n, d) + c.genNewSeq(n) + of mNewSeqOfCap: c.genNewSeqOfCap(n, d) + of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) + of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: + genArrayLen(c, n, d) + of mMulI: genBinaryOp(c, n, d, Mul) + of mDivI: genBinaryOp(c, n, d, Div) + of mModI: genBinaryOp(c, n, d, Mod) + of mAddF64: genBinaryOp(c, n, d, Add) + of mSubF64: genBinaryOp(c, n, d, Sub) + of mMulF64: genBinaryOp(c, n, d, Mul) + of mDivF64: genBinaryOp(c, n, d, Div) + of mShrI: genBinaryOp(c, n, d, BitShr) + of mShlI: genBinaryOp(c, n, d, BitShl) + of mAshrI: genBinaryOp(c, n, d, BitShr) + of mBitandI: genBinaryOp(c, n, d, BitAnd) + of mBitorI: genBinaryOp(c, n, d, BitOr) + of mBitxorI: genBinaryOp(c, n, d, BitXor) + of mAddU: genBinaryOp(c, n, d, Add) + of mSubU: genBinaryOp(c, n, d, Sub) + of mMulU: genBinaryOp(c, n, d, Mul) + of mDivU: genBinaryOp(c, n, d, Div) + of mModU: genBinaryOp(c, n, d, Mod) + of mEqI, mEqB, mEqEnum, mEqCh: + genCmpOp(c, n, d, Eq) + of mLeI, mLeEnum, mLeCh, mLeB: + genCmpOp(c, n, d, Le) + of mLtI, mLtEnum, mLtCh, mLtB: + genCmpOp(c, n, d, Lt) + of mEqF64: genCmpOp(c, n, d, Eq) + of mLeF64: genCmpOp(c, n, d, Le) + of mLtF64: genCmpOp(c, n, d, Lt) + of mLePtr, mLeU: genCmpOp(c, n, d, Le) + of mLtPtr, mLtU: genCmpOp(c, n, d, Lt) + of mEqProc, mEqRef: + genCmpOp(c, n, d, Eq) + of mXor: genBinaryOp(c, n, d, BitXor) + of mNot: genUnaryOp(c, n, d, BoolNot) + of mUnaryMinusI, mUnaryMinusI64: + genUnaryMinus(c, n, d) + #genNarrow(c, n, d) + of mUnaryMinusF64: genUnaryMinus(c, n, d) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d) + of mBitnotI: + genUnaryOp(c, n, d, BitNot) + when false: + # XXX genNarrowU modified, do not narrow signed types + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, d, TRegister(size*8)) + of mStrToStr, mEnsureMove: c.gen n[1], d + of mIntToStr: genUnaryCp(c, n, d, "nimIntToStr") + of mInt64ToStr: genUnaryCp(c, n, d, "nimInt64ToStr") + of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr") + of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr") + of mFloatToStr: + if n[1].typ.skipTypes(abstractInst).kind == tyFloat32: + genUnaryCp(c, n, d, "nimFloat32ToStr") + else: + genUnaryCp(c, n, d, "nimFloatToStr") + of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr") + of mEnumToStr: genEnumToStr(c, n, d) + + of mEqStr: genBinaryCp(c, n, d, "eqStrings") + of mEqCString: genCall(c, n, d) + of mLeStr: genBinaryCp(c, n, d, "leStrings") + of mLtStr: genBinaryCp(c, n, d, "ltStrings") + + of mSetLengthStr: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "setLengthStrV2") + + of mSetLengthSeq: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genCall(c, nb, d) + + of mSwap: + unused(c, n, d) + c.gen(lowerSwap(c.m.graph, n, c.m.idgen, + if c.prc == nil: c.m.module else: c.prc), d) + of mParseBiggestFloat: + genCall c, n, d + of mHigh: + c.genHigh n, d + + of mEcho: + unused(c, n, d) + genUnaryCp c, n, d, "echoBinSafe" + + of mAppendStrCh: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "nimAddCharV1") + of mMinI, mMaxI, mAbsI, mDotDot: + c.genCall(n, d) + of mSizeOf: + localError(c.config, n.info, sizeOfLikeMsg("sizeof")) + of mAlignOf: + localError(c.config, n.info, sizeOfLikeMsg("alignof")) + of mOffsetOf: + localError(c.config, n.info, sizeOfLikeMsg("offsetof")) + of mRunnableExamples: + discard "just ignore any call to runnableExamples" + of mDestroy, mTrace: discard "ignore calls to the default destructor" + of mOf: genOf(c, n, d) + of mAppendStrStr: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "nimAddStrV1") + of mAppendSeqElem: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genCall(c, nb, d) + of mIsNil: genIsNil(c, n, d) + of mInSet: genInSet(c, n, d) + of mCard: genCard(c, n, d) + of mEqSet: genEqSet(c, n, d) + of mLeSet: genLeSet(c, n, d) + of mLtSet: genLtSet(c, n, d) + of mMulSet: genBinarySet(c, n, d, m) + of mPlusSet: genBinarySet(c, n, d, m) + of mMinusSet: genBinarySet(c, n, d, m) + of mIncl, mExcl: + unused(c, n, d) + genInclExcl(c, n, m) + of mConStrStr: genStrConcat(c, n, d) + of mDefault, mZeroDefault: + genDefault c, n, d + else: + # mGCref, mGCunref, + globalError(c.config, n.info, "cannot generate code for: " & $m) + +#[ + + of mReset: + unused(c, n, d) + var d = c.genx(n[1]) + # XXX use ldNullOpcode() here? + c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) + c.gABC(n, opcNodeToReg, d, d) + c.gABx(n, ldNullOpcode(n.typ), d, c.genType(n.typ)) + + of mConStrStr: genVarargsABC(c, n, d, opcConcatStr) + + of mRepr: genUnaryABC(c, n, d, opcRepr) + + of mSlice: + var + d = c.genx(n[1]) + left = c.genIndex(n[2], n[1].typ) + right = c.genIndex(n[3], n[1].typ) + if isEmpty(d): d = c.getTemp(n) + c.gABC(n, opcNodeToReg, d, d) + c.gABC(n, opcSlice, d, left, right) + c.freeTemp(left) + c.freeTemp(right) + c.freeTemp(d) + + of mMove: + let arg = n[1] + let a = c.genx(arg) + if isEmpty(d): d = c.getTemp(arg) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a) + c.freeTemp(a) + of mDup: + let arg = n[1] + let a = c.genx(arg) + if isEmpty(d): d = c.getTemp(arg) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a) + c.freeTemp(a) + + of mNodeId: + c.genUnaryABC(n, d, opcNodeId) + + of mExpandToAst: + if n.len != 2: + globalError(c.config, n.info, "expandToAst requires 1 argument") + let arg = n[1] + if arg.kind in nkCallKinds: + #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}: + # "ExpandToAst: expanded symbol is no macro or template" + if isEmpty(d): d = c.getTemp(n) + c.genCall(arg, d) + # do not call clearDest(n, d) here as getAst has a meta-type as such + # produces a value + else: + globalError(c.config, n.info, "expandToAst requires a call expression") + of mParseExprToAst: + genBinaryABC(c, n, d, opcParseExprToAst) + of mParseStmtToAst: + genBinaryABC(c, n, d, opcParseStmtToAst) + of mTypeTrait: + let tmp = c.genx(n[1]) + if isEmpty(d): d = c.getTemp(n) + c.gABx(n, opcSetType, tmp, c.genType(n[1].typ)) + c.gABC(n, opcTypeTrait, d, tmp) + c.freeTemp(tmp) + of mSlurp: genUnaryABC(c, n, d, opcSlurp) + of mNLen: genUnaryABI(c, n, d, opcLenSeq, nimNodeFlag) + of mGetImpl: genUnaryABC(c, n, d, opcGetImpl) + of mGetImplTransf: genUnaryABC(c, n, d, opcGetImplTransf) + of mSymOwner: genUnaryABC(c, n, d, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, d, opcSymIsInstantiationOf) + of mNChild: genBinaryABC(c, n, d, opcNChild) + of mNAdd: genBinaryABC(c, n, d, opcNAdd) + of mNAddMultiple: genBinaryABC(c, n, d, opcNAddMultiple) + of mNKind: genUnaryABC(c, n, d, opcNKind) + of mNSymKind: genUnaryABC(c, n, d, opcNSymKind) + + of mNccValue: genUnaryABC(c, n, d, opcNccValue) + of mNccInc: genBinaryABC(c, n, d, opcNccInc) + of mNcsAdd: genBinaryABC(c, n, d, opcNcsAdd) + of mNcsIncl: genBinaryABC(c, n, d, opcNcsIncl) + of mNcsLen: genUnaryABC(c, n, d, opcNcsLen) + of mNcsAt: genBinaryABC(c, n, d, opcNcsAt) + of mNctLen: genUnaryABC(c, n, d, opcNctLen) + of mNctGet: genBinaryABC(c, n, d, opcNctGet) + of mNctHasNext: genBinaryABC(c, n, d, opcNctHasNext) + of mNctNext: genBinaryABC(c, n, d, opcNctNext) + + of mNIntVal: genUnaryABC(c, n, d, opcNIntVal) + of mNFloatVal: genUnaryABC(c, n, d, opcNFloatVal) + of mNSymbol: genUnaryABC(c, n, d, opcNSymbol) + of mNIdent: genUnaryABC(c, n, d, opcNIdent) + of mNGetType: + let tmp = c.genx(n[1]) + if isEmpty(d): d = c.getTemp(n) + let rc = case n[0].sym.name.s: + of "getType": 0 + of "typeKind": 1 + of "getTypeInst": 2 + else: 3 # "getTypeImpl" + c.gABC(n, opcNGetType, d, tmp, rc) + c.freeTemp(tmp) + #genUnaryABC(c, n, d, opcNGetType) + of mNSizeOf: + let imm = case n[0].sym.name.s: + of "getSize": 0 + of "getAlign": 1 + else: 2 # "getOffset" + c.genUnaryABI(n, d, opcNGetSize, imm) + of mNStrVal: genUnaryABC(c, n, d, opcNStrVal) + of mNSigHash: genUnaryABC(c, n , d, opcNSigHash) + of mNSetIntVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetIntVal) + of mNSetFloatVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetFloatVal) + of mNSetSymbol: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetSymbol) + of mNSetIdent: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetIdent) + of mNSetStrVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetStrVal) + of mNNewNimNode: genBinaryABC(c, n, d, opcNNewNimNode) + of mNCopyNimNode: genUnaryABC(c, n, d, opcNCopyNimNode) + of mNCopyNimTree: genUnaryABC(c, n, d, opcNCopyNimTree) + of mNBindSym: genBindSym(c, n, d) + of mStrToIdent: genUnaryABC(c, n, d, opcStrToIdent) + of mEqIdent: genBinaryABC(c, n, d, opcEqIdent) + of mEqNimrodNode: genBinaryABC(c, n, d, opcEqNimNode) + of mSameNodeType: genBinaryABC(c, n, d, opcSameNodeType) + of mNLineInfo: + case n[0].sym.name.s + of "getFile": genUnaryABI(c, n, d, opcNGetLineInfo, 0) + of "getLine": genUnaryABI(c, n, d, opcNGetLineInfo, 1) + of "getColumn": genUnaryABI(c, n, d, opcNGetLineInfo, 2) + of "copyLineInfo": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNCopyLineInfo) + of "setLine": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoLine) + of "setColumn": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoColumn) + of "setFile": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoFile) + else: internalAssert c.config, false + of mNHint: + unused(c, n, d) + genBinaryStmt(c, n, opcNHint) + of mNWarning: + unused(c, n, d) + genBinaryStmt(c, n, opcNWarning) + of mNError: + if n.len <= 1: + # query error condition: + c.gABC(n, opcQueryErrorFlag, d) + else: + # setter + unused(c, n, d) + genBinaryStmt(c, n, opcNError) + of mNCallSite: + if isEmpty(d): d = c.getTemp(n) + c.gABC(n, opcCallSite, d) + of mNGenSym: genBinaryABC(c, n, d, opcGenSym) + +]# + +proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = + result = nil + case n[0].kind + of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: + var m = n[0][0] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n[0]) + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + var m = n[0][1] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n[0]) + result.add n[0][0] + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) + else: + if n[0].kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( deref ( x )) --> x + result = n[0][0] + +template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = + if isEmpty(d): + body(Tree d) + else: + buildTyped c.code, info, Asgn, typeToIr(c.m.types, typ): + copyTree c.code, d + body(c.code) + +proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) = + if (let m = canElimAddr(n, c.m.idgen); m != nil): + gen(c, m, d, flags) + return + + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[0], flags) + template body(target) = + buildTyped target, info, AddrOf, typeToIr(c.m.types, n.typ): + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[0], flags) + template body(target) = + buildTyped target, info, Load, typeToIr(c.m.types, n.typ): + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) = + let targetType = n.typ.skipTypes({tyDistinct}) + let argType = arg.typ.skipTypes({tyDistinct}) + + if sameBackendType(targetType, argType) or ( + argType.kind == tyProc and targetType.kind == argType.kind): + # don't do anything for lambda lifting conversions: + gen c, arg, d + return + + let info = toLineInfo(c, n.info) + let tmp = c.genx(arg, flags) + template body(target) = + buildTyped target, info, opc, typeToIr(c.m.types, n.typ): + if opc == CheckedObjConv: + target.addLabel info, CheckedGoto, c.exitLabel + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) = + # XXX x = (x.old, 22) produces wrong code ... stupid self assignments + let info = toLineInfo(c, n.info) + template body(target) = + buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ): + for i in ord(n.kind == nkObjConstr)..<n.len: + let it = n[i] + if it.kind == nkExprColonExpr: + genField(c, it[0], Value target) + let tmp = c.genx(it[1]) + copyTree target, tmp + c.freeTemp(tmp) + else: + let tmp = c.genx(it) + target.addImmediateVal info, i + copyTree target, tmp + c.freeTemp(tmp) + + valueIntoDest c, info, d, n.typ, body + +proc genArrayConstr(c: var ProcCon; n: PNode, d: var Value) = + let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) + if seqType.kind == tySequence: + localError c.config, n.info, "sequence constructor not implemented" + return + + let info = toLineInfo(c, n.info) + template body(target) = + buildTyped target, info, ArrayConstr, typeToIr(c.m.types, n.typ): + for i in 0..<n.len: + let tmp = c.genx(n[i]) + copyTree target, tmp + c.freeTemp(tmp) + + valueIntoDest c, info, d, n.typ, body + +proc genAsgn2(c: var ProcCon; a, b: PNode) = + assert a != nil + assert b != nil + var d = c.genx(a) + c.gen b, d + +proc genVarSection(c: var ProcCon; n: PNode) = + for a in n: + if a.kind == nkCommentStmt: continue + #assert(a[0].kind == nkSym) can happen for transformed vars + if a.kind == nkVarTuple: + c.gen(lowerTupleUnpacking(c.m.graph, a, c.m.idgen, c.prc)) + else: + var vn = a[0] + if vn.kind == nkPragmaExpr: vn = vn[0] + if vn.kind == nkSym: + let s = vn.sym + var opc: Opcode + if sfThread in s.flags: + opc = SummonThreadLocal + elif sfGlobal in s.flags: + opc = SummonGlobal + else: + opc = Summon + c.code.addSummon toLineInfo(c, a.info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), opc + if a[2].kind != nkEmpty: + genAsgn2(c, vn, a[2]) + else: + if a[2].kind == nkEmpty: + discard "XXX assign default value to location here" + else: + genAsgn2(c, vn, a[2]) + +proc genAsgn(c: var ProcCon; n: PNode) = + var d = c.genx(n[0]) + c.gen n[1], d + +proc convStrToCStr(c: var ProcCon; n: PNode; d: var Value) = + genUnaryCp(c, n, d, "nimToCStringConv", argAt = 0) + +proc convCStrToStr(c: var ProcCon; n: PNode; d: var Value) = + genUnaryCp(c, n, d, "cstrToNimstr", argAt = 0) + +proc irModule(c: var ProcCon; owner: PSym): string = + #if owner == c.m.module: "" else: + customPath(toFullPath(c.config, owner.info)) + +proc genRdVar(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let s = n.sym + if ast.originatingModule(s) != c.m.module: + template body(target) = + build target, info, ModuleSymUse: + target.addStrVal c.m.strings, info, irModule(c, ast.originatingModule(s)) + target.addImmediateVal info, s.itemId.item.int + + valueIntoDest c, info, d, s.typ, body + else: + template body(target) = + target.addSymUse info, SymId(s.itemId.item) + valueIntoDest c, info, d, s.typ, body + +proc genSym(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = + let s = n.sym + case s.kind + of skVar, skForVar, skTemp, skLet, skResult, skParam, skConst: + genRdVar(c, n, d, flags) + of skProc, skFunc, skConverter, skMethod, skIterator: + if ast.originatingModule(s) == c.m.module: + # anon and generic procs have no AST so we need to remember not to forget + # to emit these: + if not c.m.pendingProcs.hasKey(s.itemId): + c.m.pendingProcs[s.itemId] = s + genRdVar(c, n, d, flags) + of skEnumField: + let info = toLineInfo(c, n.info) + template body(target) = + target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), s.position + valueIntoDest c, info, d, n.typ, body + else: + localError(c.config, n.info, "cannot generate code for: " & s.name.s) + +proc genNumericLit(c: var ProcCon; n: PNode; d: var Value; bits: int64) = + let info = toLineInfo(c, n.info) + template body(target) = + target.addIntVal c.m.integers, info, typeToIr(c.m.types, n.typ), bits + valueIntoDest c, info, d, n.typ, body + +proc genStringLit(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + template body(target) = + target.addStrVal c.m.strings, info, n.strVal + valueIntoDest c, info, d, n.typ, body + +proc genNilLit(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + template body(target) = + target.addNilVal info, typeToIr(c.m.types, n.typ) + valueIntoDest c, info, d, n.typ, body + +proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = + # XXX to implement properly + gen c, n[0], d + +proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let arrayKind = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind + let info = toLineInfo(c, n.info) + case arrayKind + of tyString: + # XXX implement range check + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types): + copyTree target, a + target.addImmediateVal info, 1 # (len, p)-pair + copyTree target, b + intoDest d, info, t, body + freeTemp c, b + freeTemp c, a + + of tyCstring, tyPtr, tyUncheckedArray: + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + template body(target) = + buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ): + copyTree target, a + copyTree target, b + valueIntoDest c, info, d, n.typ, body + + freeTemp c, b + freeTemp c, a + of tyTuple: + let a = genx(c, n[0], flags) + let b = int n[1].intVal + template body(target) = + buildTyped target, info, FieldAt, typeToIr(c.m.types, n.typ): + copyTree target, a + target.addImmediateVal info, b + valueIntoDest c, info, d, n.typ, body + + freeTemp c, a + of tyOpenArray, tyVarargs: + # XXX implement range check + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, openArrayPayloadType(c.m.types, n[0].typ): + copyTree target, a + target.addImmediateVal info, 0 # (p, len)-pair + copyTree target, b + intoDest d, info, t, body + + freeTemp c, b + freeTemp c, a + of tyArray: + # XXX implement range check + let a = genx(c, n[0], flags) + var b = default(Value) + genIndex(c, n[1], n[0].typ, b) + + template body(target) = + buildTyped target, info, ArrayAt, typeToIr(c.m.types, n.typ): + copyTree target, a + copyTree target, b + valueIntoDest c, info, d, n.typ, body + freeTemp c, b + freeTemp c, a + of tySequence: + let a = genx(c, n[0], flags) + let b = genx(c, n[1]) + let t = typeToIr(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, n[0].typ): + copyTree target, a + target.addImmediateVal info, 1 # (len, p)-pair + copyTree target, b + intoDest d, info, t, body + freeTemp c, b + freeTemp c, a + else: + localError c.config, n.info, "invalid type for nkBracketExpr: " & $arrayKind + +proc genObjAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let a = genx(c, n[0], flags) + + template body(target) = + buildTyped target, info, FieldAt, typeToIr(c.m.types, n.typ): + copyTree target, a + genField c, n[1], Value(target) + + valueIntoDest c, info, d, n.typ, body + freeTemp c, a + +proc genParams(c: var ProcCon; params: PNode) = + for i in 1..<params.len: + let s = params[i].sym + c.code.addSummon toLineInfo(c, params[i].info), SymId(s.itemId.item), typeToIr(c.m.types, s.typ), SummonParam + +proc addCallConv(c: var ProcCon; info: PackedLineInfo; callConv: TCallingConvention) = + template ann(s: untyped) = c.code.addPragmaId info, s + case callConv + of ccNimCall, ccFastCall, ccClosure: ann FastCall + of ccStdCall: ann StdCall + of ccCDecl: ann CDeclCall + of ccSafeCall: ann SafeCall + of ccSysCall: ann SysCall + of ccInline: ann InlineCall + of ccNoInline: ann NoinlineCall + of ccThisCall: ann ThisCall + of ccNoConvention: ann NoCall + +proc genProc(cOuter: var ProcCon; n: PNode) = + if n.len == 0 or n[namePos].kind != nkSym: return + let prc = n[namePos].sym + if isGenericRoutineStrict(prc): return + + var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config) + genParams(c, prc.typ.n) + + let body = transformBody(c.m.graph, c.m.idgen, prc, useCache) + + let info = toLineInfo(c, body.info) + build c.code, info, ProcDecl: + addSymDef c.code, info, SymId(prc.itemId.item) + addCallConv c, info, prc.typ.callConv + if {sfImportc, sfExportc} * prc.flags != {}: + build c.code, info, PragmaPair: + c.code.addPragmaId info, ExternName + c.code.addStrVal c.m.strings, info, prc.loc.r + if sfImportc in prc.flags: + if lfHeader in prc. loc.flags: + assert(prc. annex != nil) + let str = getStr(prc. annex.path) + build c.code, info, PragmaPair: + c.code.addPragmaId info, HeaderImport + c.code.addStrVal c.m.strings, info, str + elif lfDynamicLib in prc. loc.flags: + assert(prc. annex != nil) + let str = getStr(prc. annex.path) + build c.code, info, PragmaPair: + c.code.addPragmaId info, DllImport + c.code.addStrVal c.m.strings, info, str + elif sfExportc in prc.flags: + if lfDynamicLib in prc. loc.flags: + c.code.addPragmaId info, DllExport + else: + c.code.addPragmaId info, ObjExport + + gen(c, body) + patch c, body, c.exitLabel + + copyTree cOuter.code, c.code + +proc gen(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags = {}) = + when defined(nimCompilerStacktraceHints): + setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags + case n.kind + of nkSym: genSym(c, n, d, flags) + of nkCallKinds: + if n[0].kind == nkSym: + let s = n[0].sym + if s.magic != mNone: + genMagic(c, n, d, s.magic) + elif s.kind == skMethod: + localError(c.config, n.info, "cannot call method " & s.name.s & + " at compile time") + else: + genCall(c, n, d) + clearDest(c, n, d) + else: + genCall(c, n, d) + clearDest(c, n, d) + of nkCharLit..nkInt64Lit, nkUIntLit..nkUInt64Lit: + genNumericLit(c, n, d, n.intVal) + of nkFloatLit..nkFloat128Lit: + genNumericLit(c, n, d, cast[int64](n.floatVal)) + of nkStrLit..nkTripleStrLit: + genStringLit(c, n, d) + of nkNilLit: + if not n.typ.isEmptyType: genNilLit(c, n, d) + else: unused(c, n, d) + of nkAsgn, nkFastAsgn, nkSinkAsgn: + unused(c, n, d) + genAsgn(c, n) + of nkDotExpr: genObjAccess(c, n, d, flags) + of nkCheckedFieldExpr: genObjAccess(c, n[0], d, flags) + of nkBracketExpr: genArrAccess(c, n, d, flags) + of nkDerefExpr, nkHiddenDeref: genDeref(c, n, d, flags) + of nkAddr, nkHiddenAddr: genAddr(c, n, d, flags) + of nkIfStmt, nkIfExpr: genIf(c, n, d) + of nkWhenStmt: + # This is "when nimvm" node. Chose the first branch. + gen(c, n[0][1], d) + of nkCaseStmt: genCase(c, n, d) + of nkWhileStmt: + unused(c, n, d) + genWhile(c, n) + of nkBlockExpr, nkBlockStmt: genBlock(c, n, d) + of nkReturnStmt: genReturn(c, n) + of nkRaiseStmt: genRaise(c, n) + of nkBreakStmt: genBreak(c, n) + of nkTryStmt, nkHiddenTryStmt: genTry(c, n, d) + of nkStmtList: + #unused(c, n, d) + # XXX Fix this bug properly, lexim triggers it + for x in n: gen(c, x) + of nkStmtListExpr: + for i in 0..<n.len-1: gen(c, n[i]) + gen(c, n[^1], d, flags) + of nkPragmaBlock: + gen(c, n.lastSon, d, flags) + of nkDiscardStmt: + unused(c, n, d) + gen(c, n[0], d) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + genConv(c, n, n[1], d, flags, NumberConv) # misnomer? + of nkObjDownConv: + genConv(c, n, n[0], d, flags, ObjConv) + of nkObjUpConv: + genConv(c, n, n[0], d, flags, CheckedObjConv) + of nkVarSection, nkLetSection, nkConstSection: + unused(c, n, d) + genVarSection(c, n) + of nkLambdaKinds: + #let s = n[namePos].sym + #discard genProc(c, s) + gen(c, newSymNode(n[namePos].sym), d) + of nkChckRangeF, nkChckRange64, nkChckRange: + genRangeCheck(c, n, d) + of declarativeDefs - {nkIteratorDef}: + unused(c, n, d) + genProc(c, n) + of nkEmpty, nkCommentStmt, nkTypeSection, nkPragma, + nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, + nkMixinStmt, nkBindStmt, nkMacroDef, nkIteratorDef: + unused(c, n, d) + of nkStringToCString: convStrToCStr(c, n, d) + of nkCStringToString: convCStrToStr(c, n, d) + of nkBracket: genArrayConstr(c, n, d) + of nkCurly: genSetConstr(c, n, d) + of nkObjConstr, nkPar, nkClosure, nkTupleConstr: + genObjOrTupleConstr(c, n, d) + of nkCast: + genConv(c, n, n[1], d, flags, Cast) + of nkComesFrom: + discard "XXX to implement for better stack traces" + else: + localError(c.config, n.info, "cannot generate IR code for " & $n) + +proc genStmt*(c: var ProcCon; n: PNode): int = + result = c.code.len + var d = default(Value) + c.gen(n, d) + unused c, n, d + +proc genExpr*(c: var ProcCon; n: PNode, requiresValue = true): int = + result = c.code.len + var d = default(Value) + c.gen(n, d) + if isEmpty d: + if requiresValue: + globalError(c.config, n.info, "VM problem: d register is not set") diff --git a/compiler/nir/cir.nim b/compiler/nir/cir.nim new file mode 100644 index 000000000000..90a3035ddb65 --- /dev/null +++ b/compiler/nir/cir.nim @@ -0,0 +1,78 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# We produce C code as a list of tokens. + +import std / assertions +import .. / ic / bitabs + +type + Token = LitId # indexing into the tokens BiTable[string] + + PredefinedToken = enum + IgnoreMe = "<unused>" + EmptyToken = "" + DeclPrefix = "" # the next token is the name of a definition + CurlyLe = "{" + CurlyRi = "}" + ParLe = "(" + ParRi = ")" + BracketLe = "[" + BracketRi = "]" + NewLine = "\n" + Semicolon = ";" + Comma = ", " + Space = " " + Colon = ":" + Dot = "." + Arrow = "->" + Star = "*" + Amp = "&" + AsgnOpr = " = " + ScopeOpr = "::" + ConstKeyword = "const " + StaticKeyword = "static " + NimString = "NimString" + StrLitPrefix = "(NimChar*)" + StrLitNamePrefix = "Qstr" + LoopKeyword = "while (true) " + WhileKeyword = "while (" + IfKeyword = "if (" + ElseKeyword = "else " + SwitchKeyword = "switch (" + CaseKeyword = "case " + DefaultKeyword = "default:" + BreakKeyword = "break" + NullPtr = "nullptr" + IfNot = "if (!(" + ReturnKeyword = "return " + +const + ModulePrefix = Token(int(ReturnKeyword)+1) + +proc fillTokenTable(tab: var BiTable[string]) = + for e in EmptyToken..high(PredefinedToken): + let id = tab.getOrIncl $e + assert id == LitId(e) + +type + GeneratedCode* = object + code: seq[LitId] + tokens: BiTable[string] + +proc initGeneratedCode*(): GeneratedCode = + result = GeneratedCode(code: @[], tokens: initBiTable[string]()) + fillTokenTable(result.tokens) + +proc add*(g: var GeneratedCode; t: PredefinedToken) {.inline.} = + g.code.add Token(t) + +proc add*(g: var GeneratedCode; s: string) {.inline.} = + g.code.add g.tokens.getOrIncl(s) + diff --git a/compiler/nir/nir.nim b/compiler/nir/nir.nim new file mode 100644 index 000000000000..1994a1be7247 --- /dev/null +++ b/compiler/nir/nir.nim @@ -0,0 +1,71 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Nim Intermediate Representation, designed to capture all of Nim's semantics without losing too much +## precious information. Can easily be translated into C. And to JavaScript, hopefully. + +import ".." / [ast, modulegraphs, renderer, transf] +import nirtypes, nirinsts, ast2ir + +type + PCtx* = ref object of TPassContext + m: ModuleCon + c: ProcCon + oldErrorCount: int + +proc newCtx*(module: PSym; g: ModuleGraph; idgen: IdGenerator): PCtx = + let m = initModuleCon(g, g.config, idgen, module) + PCtx(m: m, c: initProcCon(m, nil, g.config), idgen: idgen) + +proc refresh*(c: PCtx; module: PSym; idgen: IdGenerator) = + c.m = initModuleCon(c.m.graph, c.m.graph.config, idgen, module) + c.c = initProcCon(c.m, nil, c.m.graph.config) + c.idgen = idgen + +proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = + if graph.repl.isNil: + graph.repl = newCtx(module, graph, idgen) + #registerAdditionalOps(PCtx graph.repl) + else: + refresh(PCtx graph.repl, module, idgen) + +proc setupNirReplGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = + setupGlobalCtx(module, graph, idgen) + result = PCtx graph.repl + +proc evalStmt(c: PCtx; n: PNode) = + let n = transformExpr(c.m.graph, c.idgen, c.m.module, n) + let pc = genStmt(c.c, n) + + var res = "" + if pc < c.c.code.len: + toString c.c.code, NodePos(pc), c.m.strings, c.m.integers, res + #res.add "\n" + #toString res, c.m.types.g + echo res + + +proc runCode*(c: PPassContext; n: PNode): PNode = + let c = PCtx(c) + # don't eval errornous code: + if c.oldErrorCount == c.m.graph.config.errorCounter: + evalStmt(c, n) + result = newNodeI(nkEmpty, n.info) + else: + result = n + c.oldErrorCount = c.m.graph.config.errorCounter + +when false: + type + Module* = object + types: TypeGraph + data: seq[Tree] + init: seq[Tree] + procs: seq[Tree] + diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim new file mode 100644 index 000000000000..f037b4f0e0e8 --- /dev/null +++ b/compiler/nir/nirinsts.nim @@ -0,0 +1,418 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## NIR instructions. Somewhat inspired by LLVM's instructions. + +import std / [assertions, hashes] +import .. / ic / bitabs +import nirlineinfos, nirtypes + +type + SymId* = distinct int + +proc `$`*(s: SymId): string {.borrow.} +proc hash*(s: SymId): Hash {.borrow.} +proc `==`*(a, b: SymId): bool {.borrow.} + +type + Opcode* = enum + Nop, + ImmediateVal, + IntVal, + StrVal, + SymDef, + SymUse, + Typed, # with type ID + PragmaId, # with Pragma ID, possible values: see PragmaKey enum + NilVal, + Label, + Goto, + CheckedGoto, + LoopLabel, + GotoLoop, # last atom + + ModuleSymUse, # `"module".x` + + ArrayConstr, + ObjConstr, + Ret, + Yld, + + Select, + SelectPair, # ((values...), Label) + SelectList, # (values...) + SelectValue, # (value) + SelectRange, # (valueA..valueB) + SummonGlobal, + SummonThreadLocal, + Summon, # x = Summon Typed <Type ID>; x begins to live + SummonParam, + SummonConst, + Kill, # `Kill x`: scope end for `x` + + AddrOf, + ArrayAt, # addr(a[i]) + FieldAt, # addr(obj.field) + + Load, # a[] + Store, # a[] = b + Asgn, # a = b + SetExc, + TestExc, + + Call, + IndirectCall, + CheckedCall, # call that can raise + CheckedIndirectCall, # call that can raise + CheckedAdd, # with overflow checking etc. + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + BoolNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + TestOf, + Emit, + ProcDecl, + PragmaPair + +type + PragmaKey* = enum + FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall, + ExternName, + HeaderImport, + DllImport, + DllExport, + ObjExport + +const + LastAtomicValue = GotoLoop + + OpcodeBits = 8'u32 + OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 + + ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal} + + ValueProducing* = { + ImmediateVal, + IntVal, + StrVal, + SymUse, + NilVal, + ModuleSymUse, + ArrayConstr, + ObjConstr, + CheckedAdd, + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + BoolNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + AddrOf, + Load, + ArrayAt, + FieldAt, + TestOf + } + +type + Instr* = object # 8 bytes + x: uint32 + info: PackedLineInfo + +template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask) +template operand(n: Instr): uint32 = (n.x shr OpcodeBits) + +template toX(k: Opcode; operand: uint32): uint32 = + uint32(k) or (operand shl OpcodeBits) + +template toX(k: Opcode; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl OpcodeBits) + +type + Tree* = object + nodes: seq[Instr] + + Values* = object + numbers: BiTable[int64] + strings: BiTable[string] + +type + PatchPos* = distinct int + NodePos* = distinct int + +const + InvalidPatchPos* = PatchPos(-1) + +proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 + +proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos = + result = PatchPos tree.nodes.len + tree.nodes.add Instr(x: toX(kind, 1'u32), info: info) + +proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue +proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue + +proc patch*(tree: var Tree; pos: PatchPos) = + let pos = pos.int + let k = tree.nodes[pos].kind + assert k > LastAtomicValue + let distance = int32(tree.nodes.len - pos) + assert distance > 0 + tree.nodes[pos].x = toX(k, cast[uint32](distance)) + +template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) = + let pos = prepare(tree, info, kind) + body + patch(tree, pos) + +template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = + let pos = prepare(tree, info, kind) + tree.addTyped info, typ + body + patch(tree, pos) + +proc len*(tree: Tree): int {.inline.} = tree.nodes.len + +template rawSpan(n: Instr): int = int(operand(n)) + +proc nextChild(tree: Tree; pos: var int) {.inline.} = + if tree.nodes[pos].kind > LastAtomicValue: + assert tree.nodes[pos].operand > 0'u32 + inc pos, tree.nodes[pos].rawSpan + else: + inc pos + +iterator sons*(tree: Tree; n: NodePos): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + while pos < last: + yield NodePos pos + nextChild tree, pos + +template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int] + +proc span(tree: Tree; pos: int): int {.inline.} = + if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) + +proc copyTree*(dest: var Tree; src: Tree) = + let pos = 0 + let L = span(src, pos) + let d = dest.nodes.len + dest.nodes.setLen(d + L) + assert L > 0 + for i in 0..<L: + dest.nodes[d+i] = src.nodes[pos+i] + +type + LabelId* = distinct int + +proc newLabel*(labelGen: var int): LabelId {.inline.} = + result = LabelId labelGen + inc labelGen + +proc addNewLabel*(t: var Tree; labelGen: var int; info: PackedLineInfo; k: Opcode): LabelId = + assert k in {Label, LoopLabel} + result = LabelId labelGen + t.nodes.add Instr(x: toX(k, uint32(result)), info: info) + inc labelGen + +proc boolVal*(t: var Tree; info: PackedLineInfo; b: bool) = + t.nodes.add Instr(x: toX(ImmediateVal, uint32(b)), info: info) + +proc gotoLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) = + assert k in {Goto, GotoLoop, CheckedGoto} + t.nodes.add Instr(x: toX(k, uint32(L)), info: info) + +proc addLabel*(t: var Tree; info: PackedLineInfo; k: Opcode; L: LabelId) {.inline.} = + assert k in {Label, LoopLabel, Goto, GotoLoop, CheckedGoto} + t.nodes.add Instr(x: toX(k, uint32(L)), info: info) + +proc addSymUse*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} = + t.nodes.add Instr(x: toX(SymUse, uint32(s)), info: info) + +proc addSymDef*(t: var Tree; info: PackedLineInfo; s: SymId) {.inline.} = + t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) + +proc addTyped*(t: var Tree; info: PackedLineInfo; typ: TypeId) {.inline.} = + t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) + +proc addSummon*(t: var Tree; info: PackedLineInfo; s: SymId; typ: TypeId; opc = Summon) {.inline.} = + assert opc in {Summon, SummonConst, SummonGlobal, SummonThreadLocal, SummonParam} + let x = prepare(t, info, opc) + t.nodes.add Instr(x: toX(Typed, uint32(typ)), info: info) + t.nodes.add Instr(x: toX(SymDef, uint32(s)), info: info) + patch t, x + +proc addImmediateVal*(t: var Tree; info: PackedLineInfo; x: int) = + assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) + t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) + +proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) = + t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info) + +proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = + buildTyped t, info, NumberConv, typ: + t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info) + +proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) = + t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info) + +proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) = + buildTyped t, info, NumberConv, typ: + t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info) + +proc escapeToNimLit(s: string; result: var string) = + result.add '"' + for c in items s: + if c < ' ' or int(c) >= 128: + result.add '\\' + result.addInt int(c) + elif c == '\\': + result.add r"\\" + elif c == '\n': + result.add r"\n" + elif c == '\r': + result.add r"\r" + elif c == '\t': + result.add r"\t" + else: + result.add c + result.add '"' + +proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64]; + r: var string; nesting = 0) = + if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: + r.add ' ' + + case t[pos].kind + of Nop: r.add "Nop" + of ImmediateVal: + r.add $t[pos].operand + of IntVal: + r.add "IntVal " + r.add $integers[LitId t[pos].operand] + of StrVal: + escapeToNimLit(strings[LitId t[pos].operand], r) + of SymDef: + r.add "SymDef " + r.add $t[pos].operand + of SymUse: + r.add "SymUse " + r.add $t[pos].operand + of PragmaId: + r.add $cast[PragmaKey](t[pos].operand) + of Typed: + r.add "Typed " + r.add $t[pos].operand + of NilVal: + r.add "NilVal" + of Label: + r.add "L" + r.add $t[pos].operand + of Goto, CheckedGoto, LoopLabel, GotoLoop: + r.add $t[pos].kind + r.add ' ' + r.add $t[pos].operand + else: + r.add $t[pos].kind + r.add "{\n" + for i in 0..<(nesting+1)*2: r.add ' ' + for p in sons(t, pos): + toString t, p, strings, integers, r, nesting+1 + r.add "\n" + for i in 0..<nesting*2: r.add ' ' + r.add "}" + +type + Value* = distinct Tree + +proc prepare*(dest: var Value; info: PackedLineInfo; k: Opcode): PatchPos {.inline.} = + assert k in ValueProducing - ValueProducingAtoms + result = prepare(Tree(dest), info, k) + +proc patch*(dest: var Value; pos: PatchPos) {.inline.} = + patch(Tree(dest), pos) + +proc localToValue*(info: PackedLineInfo; s: SymId): Value = + result = Value(Tree()) + Tree(result).addSymUse info, s + +proc hasValue*(v: Value): bool {.inline.} = Tree(v).len > 0 + +proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0 + +proc extractTemp*(v: Value): SymId = + if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse: + result = SymId(Tree(v)[NodePos 0].operand) + else: + result = SymId(-1) + +proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src) + +proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) = + assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) + Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) + +template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) = + let pos = prepare(Tree(tree), info, kind) + body + patch(tree, pos) + +proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} = + addTyped(Tree(t), info, typ) + +template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = + let pos = prepare(tree, info, kind) + tree.addTyped info, typ + body + patch(tree, pos) + +proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) = + addStrVal(Tree(t), strings, info, s) + +proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) = + addNilVal Tree(t), info, typ diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim new file mode 100644 index 000000000000..4e86f619ec3a --- /dev/null +++ b/compiler/nir/nirlineinfos.nim @@ -0,0 +1,78 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# For the line information we use 32 bits. They are used as follows: +# Bit 0 (AsideBit): If we have inline line information or not. If not, the +# remaining 31 bits are used as an index into a seq[(LitId, int, int)]. +# +# We use 10 bits for the "file ID", this means a program can consist of as much +# as 1024 different files. (If it uses more files than that, the overflow bit +# would be set.) +# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column +# so 128 is the limit and 14 bits for the line number. +# The packed representation supports files with up to 16384 lines. +# Keep in mind that whenever any limit is reached the AsideBit is set and the real line +# information is kept in a side channel. + +import std / assertions + +const + AsideBit = 1 + FileBits = 10 + LineBits = 14 + ColBits = 7 + FileMax = (1 shl FileBits) - 1 + LineMax = (1 shl LineBits) - 1 + ColMax = (1 shl ColBits) - 1 + +static: + assert AsideBit + FileBits + LineBits + ColBits == 32 + +import .. / ic / bitabs # for LitId + +type + PackedLineInfo* = distinct uint32 + + LineInfoManager* = object + aside*: seq[(LitId, int32, int32)] + +proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo = + if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax: + let col = if col < 0'i32: 0'u32 else: col.uint32 + let line = if line < 0'i32: 0'u32 else: line.uint32 + # use inline representation: + result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or + (col shl uint32(AsideBit + FileBits + LineBits))) + else: + result = PackedLineInfo((m.aside.len shl 1) or AsideBit) + m.aside.add (file, line, col) + +proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) = + let i = i.uint32 + if (i and 1'u32) == 0'u32: + # inline representation: + result = (LitId((i shr 1'u32) and FileMax.uint32), + int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32), + int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32)) + else: + result = m.aside[int(i shr 1'u32)] + +proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId = + result = unpack(m, i)[0] + +when isMainModule: + var m = LineInfoManager(aside: @[]) + for i in 0'i32..<16388'i32: + for col in 0'i32..<100'i32: + let packed = pack(m, LitId(1023), i, col) + let u = unpack(m, packed) + assert u[0] == LitId(1023) + assert u[1] == i + assert u[2] == col + echo m.aside.len diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim new file mode 100644 index 000000000000..256c25a190a2 --- /dev/null +++ b/compiler/nir/nirslots.nim @@ -0,0 +1,105 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Management of slots. Similar to "register allocation" +## in lower level languages. + +import std / [assertions, tables] +import nirtypes, nirinsts + +type + SlotManagerFlag* = enum + ReuseTemps, + ReuseVars + SlotKind* = enum + Temp, Perm + SlotManager* = object # "register allocator" + live: Table[SymId, (SlotKind, TypeId)] + dead: Table[TypeId, seq[SymId]] + flags: set[SlotManagerFlag] + inScope: seq[SymId] + locGen: ref int + +proc initSlotManager*(flags: set[SlotManagerFlag]; generator: ref int): SlotManager {.inline.} = + SlotManager(flags: flags, locGen: generator) + +proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind): SymId {.inline.} = + if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0: + result = m.dead[t].pop() + else: + result = SymId(m.locGen[]) + inc m.locGen[] + m.inScope.add result + m.live[result] = (k, t) + +proc allocTemp*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseTemps, Temp) + +proc allocVar*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseVars, Perm) + +proc freeLoc*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + assert t[1].int != 0 + m.live.del s + m.dead.mgetOrPut(t[1], @[]).add s + +proc freeTemp*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + if t[1].int != 0 and t[0] == Temp: + m.live.del s + m.dead.mgetOrPut(t[1], @[]).add s + +iterator stillAlive*(m: SlotManager): (SymId, TypeId) = + for k, v in pairs(m.live): + yield (k, v[1]) + +proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1] + +proc openScope*(m: var SlotManager) = + m.inScope.add SymId(-1) # add marker + +proc closeScope*(m: var SlotManager) = + var i = m.inScope.len - 1 + while i >= 0: + if m.inScope[i] == SymId(-1): + m.inScope.setLen i + break + dec i + +when isMainModule: + var m = initSlotManager({ReuseTemps}, new(int)) + + var g = initTypeGraph() + + let a = g.openType ArrayTy + g.addBuiltinType Int8Id + g.addArrayLen 5'u64 + let finalArrayType = sealType(g, a) + + let obj = g.openType ObjectDecl + g.addName "MyType" + + g.addField "p", finalArrayType + let objB = sealType(g, obj) + + let x = m.allocTemp(objB) + assert x.int == 0 + + let y = m.allocTemp(objB) + assert y.int == 1 + + let z = m.allocTemp(Int8Id) + assert z.int == 2 + + m.freeLoc y + let y2 = m.allocTemp(objB) + assert y2.int == 1 + + diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim new file mode 100644 index 000000000000..d989397a6080 --- /dev/null +++ b/compiler/nir/nirtypes.nim @@ -0,0 +1,347 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Type system for NIR. Close to C's type system but without its quirks. + +import std / [assertions, hashes] +import .. / ic / bitabs + +type + NirTypeKind* = enum + VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal, + AnnotationVal, + VarargsTy, # the `...` in a C prototype; also the last "atom" + APtrTy, # pointer to aliasable memory + UPtrTy, # pointer to unique/unaliasable memory + AArrayPtrTy, # pointer to array of aliasable memory + UArrayPtrTy, # pointer to array of unique/unaliasable memory + ArrayTy, + LastArrayTy, # array of unspecified size as a last field inside an object + ObjectTy, + UnionTy, + ProcTy, + ObjectDecl, + UnionDecl, + FieldDecl + +const + TypeKindBits = 8'u32 + TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32 + +type + TypeNode* = object # 4 bytes + x: uint32 + +template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask) +template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits) + +template toX(k: NirTypeKind; operand: uint32): uint32 = + uint32(k) or (operand shl TypeKindBits) + +template toX(k: NirTypeKind; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl TypeKindBits) + +type + TypeId* = distinct int + +proc `==`*(a, b: TypeId): bool {.borrow.} +proc hash*(a: TypeId): Hash {.borrow.} + +type + TypeGraph* = object + nodes: seq[TypeNode] + names: BiTable[string] + numbers: BiTable[uint64] + +const + VoidId* = TypeId 0 + Bool8Id* = TypeId 1 + Char8Id* = TypeId 2 + Int8Id* = TypeId 3 + Int16Id* = TypeId 4 + Int32Id* = TypeId 5 + Int64Id* = TypeId 6 + UInt8Id* = TypeId 7 + UInt16Id* = TypeId 8 + UInt32Id* = TypeId 9 + UInt64Id* = TypeId 10 + Float32Id* = TypeId 11 + Float64Id* = TypeId 12 + VoidPtrId* = TypeId 13 + LastBuiltinId* = 13 + +proc initTypeGraph*(): TypeGraph = + result = TypeGraph(nodes: @[ + TypeNode(x: toX(VoidTy, 0'u32)), + TypeNode(x: toX(BoolTy, 8'u32)), + TypeNode(x: toX(CharTy, 8'u32)), + TypeNode(x: toX(IntTy, 8'u32)), + TypeNode(x: toX(IntTy, 16'u32)), + TypeNode(x: toX(IntTy, 32'u32)), + TypeNode(x: toX(IntTy, 64'u32)), + TypeNode(x: toX(UIntTy, 8'u32)), + TypeNode(x: toX(UIntTy, 16'u32)), + TypeNode(x: toX(UIntTy, 32'u32)), + TypeNode(x: toX(UIntTy, 64'u32)), + TypeNode(x: toX(FloatTy, 32'u32)), + TypeNode(x: toX(FloatTy, 64'u32)), + TypeNode(x: toX(APtrTy, 2'u32)), + TypeNode(x: toX(VoidTy, 0'u32)) + ]) + assert result.nodes.len == LastBuiltinId+2 + +type + TypePatchPos* = distinct int + +const + InvalidTypePatchPos* = TypePatchPos(-1) + LastAtomicValue = VarargsTy + +proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1 + +proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = + result = TypePatchPos tree.nodes.len + tree.nodes.add TypeNode(x: toX(kind, 1'u32)) + +proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue +proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue + +proc patch(tree: var TypeGraph; pos: TypePatchPos) = + let pos = pos.int + let k = tree.nodes[pos].kind + assert k > LastAtomicValue + let distance = int32(tree.nodes.len - pos) + assert distance > 0 + tree.nodes[pos].x = toX(k, cast[uint32](distance)) + +proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len + +template rawSpan(n: TypeNode): int = int(operand(n)) + +proc nextChild(tree: TypeGraph; pos: var int) {.inline.} = + if tree.nodes[pos].kind > LastAtomicValue: + assert tree.nodes[pos].operand > 0'u32 + inc pos, tree.nodes[pos].rawSpan + else: + inc pos + +iterator sons*(tree: TypeGraph; n: TypeId): TypeId = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + while pos < last: + yield TypeId pos + nextChild tree, pos + +template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int] + +proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = + assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy} + result = TypeId(n.int+1) + +proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind + +proc span(tree: TypeGraph; pos: int): int {.inline.} = + if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) + +proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + result = (TypeId a, TypeId b) + +proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + let c = b + span(tree, b) + result = (TypeId a, TypeId b, TypeId c) + +proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt = + assert tree[n].kind == ArrayTy + result = tree.numbers[LitId tree[n].operand] + +proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = + assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, + ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl, + FieldDecl} + result = prepare(tree, kind) + +proc sealType*(tree: var TypeGraph; p: TypePatchPos): TypeId = + # TODO: Search for an existing instance of this type in + # order to reduce memory consumption. + result = TypeId(p) + patch tree, p + +proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId = + assert kind in {ObjectTy, UnionTy} + result = TypeId tree.nodes.len + tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + +proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) = + assert kind in {ObjectTy, UnionTy} + tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + +proc addVarargs*(tree: var TypeGraph) = + tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32)) + +proc getFloat128Type*(tree: var TypeGraph): TypeId = + result = TypeId tree.nodes.len + tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32)) + +proc addBuiltinType*(g: var TypeGraph; id: TypeId) = + g.nodes.add g[id] + +template firstSon(n: TypeId): TypeId = TypeId(n.int+1) + +proc addType*(g: var TypeGraph; t: TypeId) = + # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead: + if g[t].kind in {ObjectDecl, UnionDecl}: + assert g[t.firstSon].kind == NameVal + let name = LitId g[t.firstSon].operand + if g[t].kind == ObjectDecl: + g.nodes.add TypeNode(x: toX(ObjectTy, name)) + else: + g.nodes.add TypeNode(x: toX(UnionTy, name)) + else: + let pos = t.int + let L = span(g, pos) + let d = g.nodes.len + g.nodes.setLen(d + L) + assert L > 0 + for i in 0..<L: + g.nodes[d+i] = g.nodes[pos+i] + +proc addArrayLen*(g: var TypeGraph; len: uint64) = + g.nodes.add TypeNode(x: toX(IntVal, g.numbers.getOrIncl(len))) + +proc addName*(g: var TypeGraph; name: string) = + g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name))) + +proc addAnnotation*(g: var TypeGraph; name: string) = + g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl(name))) + +proc addField*(g: var TypeGraph; name: string; typ: TypeId) = + let f = g.openType FieldDecl + g.addType typ + g.addName name + discard sealType(g, f) + +proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = + let f = g.openType APtrTy + g.addType t + result = sealType(g, f) + +proc toString*(dest: var string; g: TypeGraph; i: TypeId) = + case g[i].kind + of VoidTy: dest.add "void" + of IntTy: + dest.add "i" + dest.addInt g[i].operand + of UIntTy: + dest.add "u" + dest.addInt g[i].operand + of FloatTy: + dest.add "f" + dest.addInt g[i].operand + of BoolTy: + dest.add "b" + dest.addInt g[i].operand + of CharTy: + dest.add "c" + dest.addInt g[i].operand + of NameVal, AnnotationVal: + dest.add g.names[LitId g[i].operand] + of IntVal: + dest.add $g.numbers[LitId g[i].operand] + of VarargsTy: + dest.add "..." + of APtrTy: + dest.add "aptr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of UPtrTy: + dest.add "uptr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of AArrayPtrTy: + dest.add "aArrayPtr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of UArrayPtrTy: + dest.add "uArrayPtr[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of ArrayTy: + dest.add "Array[" + let (elems, len) = g.sons2(i) + toString(dest, g, elems) + dest.add ", " + toString(dest, g, len) + dest.add "]" + of LastArrayTy: + # array of unspecified size as a last field inside an object + dest.add "LastArrayTy[" + toString(dest, g, g.elementType(i)) + dest.add "]" + of ObjectTy: + dest.add "object " + dest.add g.names[LitId g[i].operand] + of UnionTy: + dest.add "union " + dest.add g.names[LitId g[i].operand] + of ProcTy: + dest.add "proc[" + for t in sons(g, i): toString(dest, g, t) + dest.add "]" + of ObjectDecl: + dest.add "object[" + for t in sons(g, i): + toString(dest, g, t) + dest.add '\n' + dest.add "]" + of UnionDecl: + dest.add "union[" + for t in sons(g, i): + toString(dest, g, t) + dest.add '\n' + dest.add "]" + of FieldDecl: + let (typ, name) = g.sons2(i) + toString(dest, g, typ) + dest.add ' ' + toString(dest, g, name) + +proc toString*(dest: var string; g: TypeGraph) = + var i = 0 + while i < g.len: + toString(dest, g, TypeId i) + dest.add '\n' + nextChild g, i + +proc `$`(g: TypeGraph): string = + result = "" + toString(result, g) + +when isMainModule: + var g = initTypeGraph() + + let a = g.openType ArrayTy + g.addBuiltinType Int8Id + g.addArrayLen 5'u64 + let finalArrayType = sealType(g, a) + + let obj = g.openType ObjectDecl + g.nodes.add TypeNode(x: toX(NameVal, g.names.getOrIncl("MyType"))) + + g.addField "p", finalArrayType + discard sealType(g, obj) + + echo g diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim new file mode 100644 index 000000000000..6d163c6c7c33 --- /dev/null +++ b/compiler/nir/types2ir.nim @@ -0,0 +1,466 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import std / [assertions, tables, sets] +import ".." / [ast, types, options, sighashes, modulegraphs] +import nirtypes + +type + TypesCon* = object + processed: Table[ItemId, TypeId] + recursionCheck: HashSet[ItemId] + g*: TypeGraph + conf: ConfigRef + +proc initTypesCon*(conf: ConfigRef): TypesCon = + TypesCon(g: initTypeGraph(), conf: conf) + +proc mangle(c: var TypesCon; t: PType): string = + result = $sighashes.hashType(t, c.conf) + +template cached(c: var TypesCon; t: PType; body: untyped) = + result = c.processed.getOrDefault(t.itemId) + if result.int == 0: + body + c.processed[t.itemId] = result + +proc typeToIr*(c: var TypesCon; t: PType): TypeId + +proc collectFieldTypes(c: var TypesCon; n: PNode; dest: var Table[ItemId, TypeId]) = + case n.kind + of nkRecList: + for i in 0..<n.len: + collectFieldTypes(c, n[i], dest) + of nkRecCase: + assert(n[0].kind == nkSym) + collectFieldTypes(c, n[0], dest) + for i in 1..<n.len: + case n[i].kind + of nkOfBranch, nkElse: + collectFieldTypes c, lastSon(n[i]), dest + else: discard + of nkSym: + dest[n.sym.itemId] = typeToIr(c, n.sym.typ) + else: + assert false, "unknown node kind: " & $n.kind + +proc objectToIr(c: var TypesCon; n: PNode; fieldTypes: Table[ItemId, TypeId]; unionId: var int) = + case n.kind + of nkRecList: + for i in 0..<n.len: + objectToIr(c, n[i], fieldTypes, unionId) + of nkRecCase: + assert(n[0].kind == nkSym) + objectToIr(c, n[0], fieldTypes, unionId) + let u = openType(c.g, UnionDecl) + c.g.addName "u_" & $unionId + inc unionId + for i in 1..<n.len: + case n[i].kind + of nkOfBranch, nkElse: + let subObj = openType(c.g, ObjectDecl) + c.g.addName "uo_" & $unionId & "_" & $i + objectToIr c, lastSon(n[i]), fieldTypes, unionId + discard sealType(c.g, subObj) + else: discard + discard sealType(c.g, u) + of nkSym: + c.g.addField n.sym.name.s & "_" & $n.sym.position, fieldTypes[n.sym.itemId] + else: + assert false, "unknown node kind: " & $n.kind + +proc objectToIr(c: var TypesCon; t: PType): TypeId = + if t[0] != nil: + # ensure we emitted the base type: + discard typeToIr(c, t[0]) + + var unionId = 0 + var fieldTypes = initTable[ItemId, TypeId]() + collectFieldTypes c, t.n, fieldTypes + let obj = openType(c.g, ObjectDecl) + c.g.addName mangle(c, t) + if t[0] != nil: + c.g.addNominalType(ObjectTy, mangle(c, t[0])) + else: + c.g.addBuiltinType VoidId # object does not inherit + if not lacksMTypeField(t): + let f2 = c.g.openType FieldDecl + let voidPtr = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, voidPtr) + c.g.addName "m_type" + discard sealType(c.g, f2) # FieldDecl + + objectToIr c, t.n, fieldTypes, unionId + result = sealType(c.g, obj) + +proc objectHeaderToIr(c: var TypesCon; t: PType): TypeId = + result = c.g.nominalType(ObjectTy, mangle(c, t)) + +proc tupleToIr(c: var TypesCon; t: PType): TypeId = + var fieldTypes = newSeq[TypeId](t.len) + for i in 0..<t.len: + fieldTypes[i] = typeToIr(c, t[i]) + let obj = openType(c.g, ObjectDecl) + c.g.addName mangle(c, t) + for i in 0..<t.len: + c.g.addField "f_" & $i, fieldTypes[i] + result = sealType(c.g, obj) + +proc procToIr(c: var TypesCon; t: PType; addEnv = false): TypeId = + var fieldTypes = newSeq[TypeId](0) + for i in 0..<t.len: + if t[i] == nil or not isCompileTimeOnly(t[i]): + fieldTypes.add typeToIr(c, t[i]) + let obj = openType(c.g, ProcTy) + + case t.callConv + of ccNimCall, ccFastCall, ccClosure: c.g.addAnnotation "__fastcall" + of ccStdCall: c.g.addAnnotation "__stdcall" + of ccCDecl: c.g.addAnnotation "__cdecl" + of ccSafeCall: c.g.addAnnotation "__safecall" + of ccSysCall: c.g.addAnnotation "__syscall" + of ccInline: c.g.addAnnotation "__inline" + of ccNoInline: c.g.addAnnotation "__noinline" + of ccThisCall: c.g.addAnnotation "__thiscall" + of ccNoConvention: c.g.addAnnotation "" + + for i in 0..<fieldTypes.len: + c.g.addType fieldTypes[i] + + if addEnv: + let a = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, a) + + if tfVarargs in t.flags: + c.g.addVarargs() + result = sealType(c.g, obj) + +proc nativeInt(c: TypesCon): TypeId = + case c.conf.target.intSize + of 2: result = Int16Id + of 4: result = Int32Id + else: result = Int64Id + +proc openArrayPayloadType*(c: var TypesCon; t: PType): TypeId = + let e = lastSon(t) + let elementType = typeToIr(c, e) + let arr = c.g.openType AArrayPtrTy + c.g.addType elementType + result = sealType(c.g, arr) # LastArrayTy + +proc openArrayToIr(c: var TypesCon; t: PType): TypeId = + # object (a: ArrayPtr[T], len: int) + let e = lastSon(t) + let mangledBase = mangle(c, e) + let typeName = "NimOpenArray" & mangledBase + + let elementType = typeToIr(c, e) + #assert elementType.int >= 0, typeToString(t) + + let p = openType(c.g, ObjectDecl) + c.g.addName typeName + + let f = c.g.openType FieldDecl + let arr = c.g.openType AArrayPtrTy + c.g.addType elementType + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + + c.g.addField "len", c.nativeInt + + result = sealType(c.g, p) # ObjectDecl + +proc strPayloadType(c: var TypesCon): string = + result = "NimStrPayload" + let p = openType(c.g, ObjectDecl) + c.g.addName result + c.g.addField "cap", c.nativeInt + + let f = c.g.openType FieldDecl + let arr = c.g.openType LastArrayTy + c.g.addBuiltinType Char8Id + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + + discard sealType(c.g, p) + +proc strPayloadPtrType*(c: var TypesCon): TypeId = + let mangled = strPayloadType(c) + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, mangled + result = sealType(c.g, ffp) # APtrTy + +proc stringToIr(c: var TypesCon): TypeId = + #[ + + NimStrPayload = object + cap: int + data: UncheckedArray[char] + + NimStringV2 = object + len: int + p: ptr NimStrPayload + + ]# + let payload = strPayloadType(c) + + let str = openType(c.g, ObjectDecl) + c.g.addName "NimStringV2" + c.g.addField "len", c.nativeInt + + let fp = c.g.openType FieldDecl + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimStrPayload" + discard sealType(c.g, ffp) # APtrTy + c.g.addName "p" + discard sealType(c.g, fp) # FieldDecl + + result = sealType(c.g, str) # ObjectDecl + +proc seqPayloadType(c: var TypesCon; t: PType): string = + #[ + NimSeqPayload[T] = object + cap: int + data: UncheckedArray[T] + ]# + let e = lastSon(t) + result = mangle(c, e) + let payloadName = "NimSeqPayload" & result + + let elementType = typeToIr(c, e) + + let p = openType(c.g, ObjectDecl) + c.g.addName payloadName + c.g.addField "cap", c.nativeInt + + let f = c.g.openType FieldDecl + let arr = c.g.openType LastArrayTy + c.g.addType elementType + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + discard sealType(c.g, p) + +proc seqPayloadPtrType*(c: var TypesCon; t: PType): TypeId = + let mangledBase = seqPayloadType(c, t) + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase + result = sealType(c.g, ffp) # APtrTy + +proc seqToIr(c: var TypesCon; t: PType): TypeId = + #[ + NimSeqV2*[T] = object + len: int + p: ptr NimSeqPayload[T] + ]# + let mangledBase = seqPayloadType(c, t) + + let sq = openType(c.g, ObjectDecl) + c.g.addName "NimSeqV2" & mangledBase + c.g.addField "len", c.nativeInt + + let fp = c.g.openType FieldDecl + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase + discard sealType(c.g, ffp) # APtrTy + c.g.addName "p" + discard sealType(c.g, fp) # FieldDecl + + result = sealType(c.g, sq) # ObjectDecl + + +proc closureToIr(c: var TypesCon; t: PType): TypeId = + # struct {fn(args, void* env), env} + # typedef struct {$n" & + # "N_NIMCALL_PTR($2, ClP_0) $3;$n" & + # "void* ClE_0;$n} $1;$n" + let mangledBase = mangle(c, t) + let typeName = "NimClosure" & mangledBase + + let procType = procToIr(c, t, addEnv=true) + + let p = openType(c.g, ObjectDecl) + c.g.addName typeName + + let f = c.g.openType FieldDecl + c.g.addType procType + c.g.addName "ClP_0" + discard sealType(c.g, f) # FieldDecl + + let f2 = c.g.openType FieldDecl + let voidPtr = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, voidPtr) + + c.g.addName "ClE_0" + discard sealType(c.g, f2) # FieldDecl + + result = sealType(c.g, p) # ObjectDecl + +proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId = + let s = int(getSize(c.conf, t)) + case s + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = UInt32Id + of 8: result = UInt64Id + else: result = UInt8Id + +proc typeToIr*(c: var TypesCon; t: PType): TypeId = + if t == nil: return VoidId + case t.kind + of tyInt: + case int(getSize(c.conf, t)) + of 2: result = Int16Id + of 4: result = Int32Id + else: result = Int64Id + of tyInt8: result = Int8Id + of tyInt16: result = Int16Id + of tyInt32: result = Int32Id + of tyInt64: result = Int64Id + of tyFloat: + case int(getSize(c.conf, t)) + of 4: result = Float32Id + else: result = Float64Id + of tyFloat32: result = Float32Id + of tyFloat64: result = Float64Id + of tyFloat128: result = getFloat128Type(c.g) + of tyUInt: + case int(getSize(c.conf, t)) + of 2: result = UInt16Id + of 4: result = UInt32Id + else: result = UInt64Id + of tyUInt8: result = UInt8Id + of tyUInt16: result = UInt16Id + of tyUInt32: result = UInt32Id + of tyUInt64: result = UInt64Id + of tyBool: result = Bool8Id + of tyChar: result = Char8Id + of tyVoid: result = VoidId + of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange: + result = typeToIr(c, t.lastSon) + of tyEnum: + if firstOrd(c.conf, t) < 0: + result = Int32Id + else: + case int(getSize(c.conf, t)) + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = Int32Id + of 8: result = Int64Id + else: result = Int32Id + of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: + if t.len > 0: + result = typeToIr(c, t.lastSon) + else: + result = TypeId(-1) + of tyFromExpr: + if t.n != nil and t.n.typ != nil: + result = typeToIr(c, t.n.typ) + else: + result = TypeId(-1) + of tyArray: + cached(c, t): + var n = toInt64(lengthOrd(c.conf, t)) + if n <= 0: n = 1 # make an array of at least one element + let elemType = typeToIr(c, t[1]) + let a = openType(c.g, ArrayTy) + c.g.addType(elemType) + c.g.addArrayLen uint64(n) + result = sealType(c.g, a) + of tyPtr, tyRef: + cached(c, t): + let e = t.lastSon + if e.kind == tyUncheckedArray: + let elemType = typeToIr(c, e.lastSon) + let a = openType(c.g, AArrayPtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + else: + let elemType = typeToIr(c, t.lastSon) + let a = openType(c.g, APtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tyVar, tyLent: + cached(c, t): + let e = t.lastSon + if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + # skip the modifier, `var openArray` is a (ptr, len) pair too: + result = typeToIr(c, e) + else: + let elemType = typeToIr(c, e) + let a = openType(c.g, APtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tySet: + let s = int(getSize(c.conf, t)) + case s + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = UInt32Id + of 8: result = UInt64Id + else: + # array[U8, s] + cached(c, t): + let a = openType(c.g, ArrayTy) + c.g.addType(UInt8Id) + c.g.addArrayLen uint64(s) + result = sealType(c.g, a) + of tyPointer: + let a = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + result = sealType(c.g, a) + of tyObject: + # Objects are special as they can be recursive in Nim. This is easily solvable. + # We check if we are already "processing" t. If so, we produce `ObjectTy` + # instead of `ObjectDecl`. + cached(c, t): + if not c.recursionCheck.containsOrIncl(t.itemId): + result = objectToIr(c, t) + else: + result = objectHeaderToIr(c, t) + of tyTuple: + cached(c, t): + result = tupleToIr(c, t) + of tyProc: + cached(c, t): + if t.callConv == ccClosure: + result = closureToIr(c, t) + else: + result = procToIr(c, t) + of tyVarargs, tyOpenArray: + cached(c, t): + result = openArrayToIr(c, t) + of tyString: + cached(c, t): + result = stringToIr(c) + of tySequence: + cached(c, t): + result = seqToIr(c, t) + of tyCstring: + cached(c, t): + let a = openType(c.g, AArrayPtrTy) + c.g.addBuiltinType Char8Id + result = sealType(c.g, a) + of tyUncheckedArray: + # We already handled the `ptr UncheckedArray` in a special way. + cached(c, t): + let elemType = typeToIr(c, t.lastSon) + let a = openType(c.g, LastArrayTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc, + tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, + tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: + result = TypeId(-1) diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index 8517cd942658..e9ee1ee8be79 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -11,7 +11,7 @@ when not defined(leanCompiler): import std/[syncio, objectdollar, assertions, tables, strutils] import renderer import ic/replayer - +import nir/nir proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) = graph.pipelinePass = pass @@ -43,6 +43,8 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): result = nil of EvalPass, InterpreterPass: result = interpreterCode(bModule, semNode) + of NirReplPass: + result = runCode(bModule, semNode) of NonePass: raiseAssert "use setPipeLinePass to set a proper PipelinePass" @@ -112,6 +114,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator nil of EvalPass, InterpreterPass: setupEvalGen(graph, module, idgen) + of NirReplPass: + setupNirReplGen(graph, module, idgen) of GenDependPass: setupDependPass(graph, module, idgen) of Docgen2Pass: @@ -197,6 +201,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator discard finalJSCodeGen(graph, bModule, finalNode) of EvalPass, InterpreterPass: discard interpreterCode(bModule, finalNode) + of NirReplPass: + discard runCode(bModule, finalNode) of SemPass, GenDependPass: discard of Docgen2Pass, Docgen2TexPass: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 2d366d8fcb9e..ebc8be0c332d 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -68,9 +68,7 @@ proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = let assignment = initExpr[i] if assignment.kind != nkExprColonExpr: invalidObjConstr(c, assignment) - continue - - if fieldId == considerQuotedIdent(c, assignment[0]).id: + elif fieldId == considerQuotedIdent(c, assignment[0]).id: return assignment proc semConstrField(c: PContext, flags: TExprFlags, @@ -125,7 +123,7 @@ proc pickCaseBranch(caseExpr, matched: PNode): PNode = return caseExpr[i] if endsWithElse: - return caseExpr[^1] + result = caseExpr[^1] else: result = nil @@ -138,8 +136,8 @@ iterator directFieldsInRecList(recList: PNode): PNode = else: doAssert recList.kind == nkRecList for field in recList: - if field.kind != nkSym: continue - yield field + if field.kind == nkSym: + yield field template quoteStr(s: string): string = "'" & s & "'" @@ -416,8 +414,8 @@ proc semConstructTypeAux(c: PContext, if status in {initPartial, initNone, initUnknown}: discard collectMissingFields(c, t.n, constrCtx, result.defaults) let base = t[0] - if base == nil or base.id == t.id or - base.kind in { tyRef, tyPtr } and base[0].id == t.id: + if base == nil or base.id == t.id or + base.kind in {tyRef, tyPtr} and base[0].id == t.id: break t = skipTypes(base, skipPtrs) if t.kind != tyObject: @@ -463,7 +461,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType if t == nil: return localErrorNode(c, result, "object constructor needs an object type") - + if t.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and expectedType != nil and expectedType.skipTypes({tyGenericInst, diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index d14f19a8a2d7..41ec3e4809af 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -25,7 +25,7 @@ import ast, astalgo, idents, lowerings, magicsys, guards, msgs, renderer, types, modulegraphs, options, spawn, lineinfos -from trees import getMagic, isTrue, getRoot +from trees import getMagic, getRoot from strutils import `%` discard """ diff --git a/compiler/trees.nim b/compiler/trees.nim index cc2b0eafddf8..f038fbc1eb3c 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -198,10 +198,6 @@ proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode = result = newNodeI(k, n.info, b-a+1) for i in 0..b-a: result[i] = n[i+a] -proc isTrue*(n: PNode): bool = - n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or - n.kind == nkIntLit and n.intVal != 0 - proc getRoot*(n: PNode): PSym = ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be diff --git a/compiler/vm.nim b/compiler/vm.nim index 579bab600c78..85c1305e94eb 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1521,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = frame = frame.next jumpTo = findExceptionHandler(c, frame, raised) - case jumpTo.why: + case jumpTo.why of ExceptionGotoHandler: # Jump to the handler, do nothing when the `finally` block ends. savedPC = -1 diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 03265f5b559e..42ce09596895 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -321,10 +321,6 @@ proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot -proc isTrue(n: PNode): bool = - n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or - n.kind == nkIntLit and n.intVal != 0 - proc genWhile(c: PCtx; n: PNode) = # lab1: # cond, tmp diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index 48973aa5583a..872549c3b1ea 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -100,22 +100,23 @@ proc addFloatSprintf*(result: var string; x: float) = let n = writeFloatToBufferSprintf(buffer, x) result.addCstringN(cast[cstring](buffer[0].addr), n) -proc nimFloatToString(a: float): cstring = - ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 - # print `-0.0` properly - asm """ - function nimOnlyDigitsOrMinus(n) { - return n.toString().match(/^-?\d+$/); - } - if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" - else { - `result` = `a`+"" - if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" +when defined(js): + proc nimFloatToString(a: float): cstring = + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly + asm """ + function nimOnlyDigitsOrMinus(n) { + return n.toString().match(/^-?\d+$/); } - } - """ + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + else { + `result` = `a`+"" + if(nimOnlyDigitsOrMinus(`result`)){ + `result` = `a`+".0" + } + } + """ proc addFloat*(result: var string; x: float | float32) {.inline.} = ## Converts float to its string representation and appends it to `result`. diff --git a/lib/system.nim b/lib/system.nim index 3e7d84b1dfb2..8d1cda86815e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1117,8 +1117,6 @@ template sysAssert(cond: bool, msg: string) = const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript) -when notJSnotNims and hostOS != "standalone" and hostOS != "any": - include "system/cgprocs" when notJSnotNims and hasAlloc and not defined(nimSeqsV2): proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.} @@ -1431,7 +1429,6 @@ proc isNil*[T: proc | iterator {.closure.}](x: T): bool {.noSideEffect, magic: " ## Fast check whether `x` is nil. This is sometimes more efficient than ## `== nil`. - when defined(nimHasTopDownInference): # magic used for seq type inference proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} = @@ -2200,6 +2197,16 @@ when not defined(js): when notJSnotNims: when hostOS != "standalone" and hostOS != "any": + type + LibHandle = pointer # private type + ProcAddr = pointer # library loading and loading of procs: + + proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.} + proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.} + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.} + + proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.} + include "system/dyncalls" import system/countbits_impl @@ -2673,7 +2680,7 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect, proc strcmp(a, b: cstring): cint {.noSideEffect, importc, header: "<string.h>".} if pointer(x) == pointer(y): result = true - elif x.isNil or y.isNil: result = false + elif pointer(x) == nil or pointer(y) == nil: result = false else: result = strcmp(x, y) == 0 template closureScope*(body: untyped): untyped = diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index a8a4bd767843..be931ed14341 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -224,7 +224,7 @@ proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonRe proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonReloadable, inline.} = # we cannot throw an exception here! - discard c_fwrite(s, 1, cast[csize_t](s.len), f) + discard c_fwrite(s, 1, c_strlen(s), f) discard c_fflush(f) {.pop.} diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim index 9d0d248c3916..9a7645f9bff0 100644 --- a/lib/system/cgprocs.nim +++ b/lib/system/cgprocs.nim @@ -8,13 +8,3 @@ # # Headers for procs that the code generator depends on ("compilerprocs") - -type - LibHandle = pointer # private type - ProcAddr = pointer # library loading and loading of procs: - -proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.} -proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.} -proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.} - -proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.} diff --git a/lib/system/memory.nim b/lib/system/memory.nim index ebda60d8d167..156773c4843f 100644 --- a/lib/system/memory.nim +++ b/lib/system/memory.nim @@ -43,6 +43,7 @@ proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadabl inc i proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} = + if a.isNil: return 0 when useLibC: cast[int](c_strlen(a)) else: diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 60c63501cc89..89046253bba5 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -23,6 +23,14 @@ proc cmpStrings(a, b: string): int {.inline, compilerproc.} = else: result = alen - blen +proc leStrings(a, b: string): bool {.inline, compilerproc.} = + # required by upcoming backends (NIR). + cmpStrings(a, b) <= 0 + +proc ltStrings(a, b: string): bool {.inline, compilerproc.} = + # required by upcoming backends (NIR). + cmpStrings(a, b) < 0 + proc eqStrings(a, b: string): bool {.inline, compilerproc.} = let alen = a.len let blen = b.len diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index abdbcd7c3ba2..e79a2b324c0d 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -201,6 +201,10 @@ proc prepareMutation*(s: var string) {.inline.} = let s = unsafeAddr s nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[]) +proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} = + #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag): + prepareAdd(s, src.len) + appendString s, src func capacity*(self: string): int {.inline.} = ## Returns the current capacity of the string. From 68ba45cc04bedb5f39f7bff659065e137e86053d Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Wed, 11 Oct 2023 21:05:51 +0200 Subject: [PATCH 344/347] Import std/stackframes in ast2ir.nim (#22815) Ref https://github.com/nim-lang/Nim/pull/22777#issuecomment-1758090410 Co-authored-by: SirOlaf <> --- compiler/nir/ast2ir.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index f06bb3ae8103..bc7348be3756 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -17,6 +17,9 @@ import .. / ic / bitabs import nirtypes, nirinsts, nirlineinfos, nirslots, types2ir +when defined(nimCompilerStacktraceHints): + import std/stackframes + type ModuleCon* = ref object strings*: BiTable[string] From d790112ea4600c847fed830171333fde308421a3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 12 Oct 2023 03:06:25 +0800 Subject: [PATCH 345/347] update nimble (#22814) Issues like https://github.com/nim-lang/nimble/issues/1149 keep popping up. One way or another, we should alleviate the pain. Finally, we should consider https://github.com/nim-lang/nimble/pull/1141#discussion_r1316829521 as an option using some kind of cron script to update https://nim-lang.org/nimble/packages.json. It's turning into a really annoying problem. --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 571242a3631b..5fa2e8cfa778 100644 --- a/koch.nim +++ b/koch.nim @@ -11,7 +11,7 @@ const # examples of possible values for repos: Head, ea82b54 - NimbleStableCommit = "168416290e49023894fc26106799d6f1fc964a2d" # master + NimbleStableCommit = "603e329442059947d63e4c1b2ef5294f1f544485" # master AtlasStableCommit = "7b780811a168f3f32bff4822369dda46a7f87f9a" ChecksumsStableCommit = "025bcca3915a1b9f19878cea12ad68f9884648fc" From 8990626ca9715a3687b28331aee4ccf242997aa2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf <rumpf_a@web.de> Date: Thu, 12 Oct 2023 23:33:38 +0200 Subject: [PATCH 346/347] NIR: progress (#22817) Done: - [x] Implement conversions to openArray/varargs. - [x] Implement index/range checking. --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 3 - compiler/cgen.nim | 2 +- compiler/jsgen.nim | 2 +- compiler/lambdalifting.nim | 8 +- compiler/nir/ast2ir.nim | 277 +++++++++++++++++++++++++++++-------- compiler/nir/nirinsts.nim | 3 + compiler/nir/nirtypes.nim | 5 + compiler/nir/types2ir.nim | 6 +- compiler/optimizer.nim | 8 +- compiler/sempass2.nim | 4 +- compiler/transf.nim | 52 +++---- compiler/trees.nim | 3 + compiler/vm.nim | 2 +- compiler/vmgen.nim | 2 +- lib/system/strs_v2.nim | 3 + 16 files changed, 281 insertions(+), 101 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 4fe72929f7f8..3017aedcf6e8 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -2157,7 +2157,7 @@ proc toHumanStr*(kind: TTypeKind): string = ## strips leading `tk` result = toHumanStrImpl(kind, 2) -proc skipAddr*(n: PNode): PNode {.inline.} = +proc skipHiddenAddr*(n: PNode): PNode {.inline.} = (if n.kind == nkHiddenAddr: n[0] else: n) proc isNewStyleConcept*(n: PNode): bool {.inline.} = diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 44d04d7633a3..eca6fa9958a1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2279,9 +2279,6 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: binaryArith(p, e, d, m) -proc skipAddr(n: PNode): PNode = - result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n - proc genWasMoved(p: BProc; n: PNode) = var a: TLoc let n1 = n[1].skipAddr diff --git a/compiler/cgen.nim b/compiler/cgen.nim index adee8f8454b5..a2b2c429ef15 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1162,7 +1162,7 @@ proc genProcAux*(m: BModule, prc: PSym) = var returnStmt: Rope = "" assert(prc.ast != nil) - var procBody = transformBody(m.g.graph, m.idgen, prc, dontUseCache) + var procBody = transformBody(m.g.graph, m.idgen, prc, {}) if sfInjectDestructors in prc.flags: procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 1720de17f899..6b57c097f3b7 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2718,7 +2718,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = else: returnStmt = "return $#;$n" % [a.res] - var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, dontUseCache) + var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {}) if sfInjectDestructors in prc.flags: transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ee19eec08147..fdba7ba3d147 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -449,7 +449,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if innerProc: if s.isIterator: c.somethingToDo = true if not c.processed.containsOrIncl(s.id): - let body = transformBody(c.graph, c.idgen, s, useCache) + let body = transformBody(c.graph, c.idgen, s, {useCache}) detectCapturedVars(body, s, c) let ow = s.skipGenericOwner let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator @@ -755,7 +755,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass; # echo renderTree(s.getBody, {renderIds}) let oldInContainer = c.inContainer c.inContainer = 0 - var body = transformBody(d.graph, d.idgen, s, dontUseCache) + var body = transformBody(d.graph, d.idgen, s, {}) body = liftCapturedVars(body, s, d, c) if c.envVars.getOrDefault(s.id).isNil: s.transformedBody = body @@ -879,7 +879,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType; fn.typ.callConv = oldCC proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; - idgen: IdGenerator, force: bool): PNode = + idgen: IdGenerator; flags: TransformFlags): PNode = # XXX backend == backendJs does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... @@ -888,7 +888,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool; if body.kind == nkEmpty or ( g.config.backend == backendJs and not isCompileTime) or - (fn.skipGenericOwner.kind != skModule and not force): + (fn.skipGenericOwner.kind != skModule and force notin flags): # ignore forward declaration: result = body diff --git a/compiler/nir/ast2ir.nim b/compiler/nir/ast2ir.nim index bc7348be3756..fcda145ea41f 100644 --- a/compiler/nir/ast2ir.nim +++ b/compiler/nir/ast2ir.nim @@ -45,6 +45,7 @@ type locGen: int m: ModuleCon prc: PSym + options: TOptions proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; module: PSym): ModuleCon = result = ModuleCon(graph: graph, types: initTypesCon(config), slotGenerator: new(int), @@ -61,7 +62,9 @@ proc initModuleCon*(graph: ModuleGraph; config: ConfigRef; idgen: IdGenerator; m result.nativeUIntId = UInt16Id proc initProcCon*(m: ModuleCon; prc: PSym; config: ConfigRef): ProcCon = - ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config) + ProcCon(m: m, sm: initSlotManager({}, m.slotGenerator), prc: prc, config: config, + options: if prc != nil: prc.options + else: config.options) proc toLineInfo(c: var ProcCon; i: TLineInfo): PackedLineInfo = var val: LitId @@ -476,14 +479,21 @@ proc genField(c: var ProcCon; n: PNode; d: var Value) = d.addImmediateVal toLineInfo(c, n.info), pos proc genIndex(c: var ProcCon; n: PNode; arr: PType; d: var Value) = + let info = toLineInfo(c, n.info) if arr.skipTypes(abstractInst).kind == tyArray and (let x = firstOrd(c.config, arr); x != Zero): - let info = toLineInfo(c, n.info) buildTyped d, info, Sub, c.m.nativeIntId: c.gen(n, d) d.addImmediateVal toLineInfo(c, n.info), toInt(x) else: c.gen(n, d) + if optBoundsCheck in c.options: + let idx = move d + build d, info, CheckedIndex: + copyTree d.Tree, idx + let x = toInt64 lengthOrd(c.config, arr) + d.Tree.addIntVal c.m.integers, info, c.m.nativeIntId, x + d.Tree.addLabel info, CheckedGoto, c.exitLabel proc genNew(c: var ProcCon; n: PNode; needsInit: bool) = # If in doubt, always follow the blueprint of the C code generator for `mm:orc`. @@ -586,6 +596,8 @@ proc genBinaryOp(c: var ProcCon; n: PNode; d: var Value; opc: Opcode) = let t = typeToIr(c.m.types, n.typ) template body(target) = buildTyped target, info, opc, t: + if optOverflowCheck in c.options and opc in {CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedMod}: + c.code.addLabel info, CheckedGoto, c.exitLabel copyTree target, tmp copyTree target, tmp2 intoDest d, info, t, body @@ -688,10 +700,16 @@ proc genUnaryMinus(c: var ProcCon; n: PNode; d: var Value) = c.freeTemp(tmp) proc genHigh(c: var ProcCon; n: PNode; d: var Value) = - let subOpr = createMagic(c.m.graph, c.m.idgen, "-", mSubI) - let lenOpr = createMagic(c.m.graph, c.m.idgen, "len", mLengthOpenArray) - let asLenExpr = subOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(1)) - c.gen asLenExpr, d + let info = toLineInfo(c, n.info) + let t = typeToIr(c.m.types, n.typ) + var x = default(Value) + genArrayLen(c, n, x) + template body(target) = + buildTyped target, info, Sub, t: + copyTree target, x + target.addIntVal(c.m.integers, info, t, 1) + intoDest d, info, t, body + c.freeTemp x proc genBinaryCp(c: var ProcCon; n: PNode; d: var Value; compilerProc: string) = let info = toLineInfo(c, n.info) @@ -1365,6 +1383,81 @@ proc genDefault(c: var ProcCon; n: PNode; d: var Value) = let m = expandDefault(n.typ, n.info) gen c, m, d +proc genWasMoved(c: var ProcCon; n: PNode) = + let n1 = n[1].skipAddr + # XXX We need a way to replicate this logic or better yet a better + # solution for injectdestructors.nim: + #if c.withinBlockLeaveActions > 0 and notYetAlive(n1): + var d = c.genx(n1) + assert not isEmpty(d) + let m = expandDefault(n1.typ, n1.info) + gen c, m, d + +proc genMove(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let n1 = n[1].skipAddr + var a = c.genx(n1) + if n.len == 4: + # generated by liftdestructors: + let src = c.genx(n[2]) + # if ($1.p == $2.p) goto lab1 + let lab1 = newLabel(c.labelGen) + + let payloadType = seqPayloadPtrType(c.m.types, n1.typ) + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Eq, payloadType: + buildTyped c.code, info, FieldAt, payloadType: + copyTree c.code, a + c.code.addImmediateVal info, 1 # (len, p)-pair + buildTyped c.code, info, FieldAt, payloadType: + copyTree c.code, src + c.code.addImmediateVal info, 1 # (len, p)-pair + + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, true) + c.code.gotoLabel info, Goto, lab1 + + gen(c, n[3]) + c.patch n, lab1 + + buildTyped c.code, info, Asgn, typeToIr(c.m.types, n1.typ): + copyTree c.code, a + copyTree c.code, src + + else: + if isEmpty(d): d = getTemp(c, n) + buildTyped c.code, info, Asgn, typeToIr(c.m.types, n1.typ): + copyTree c.code, d + copyTree c.code, a + var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved) + if op == nil or skipTypes(n1.typ, abstractVar+{tyStatic}).kind in {tyOpenArray, tyVarargs}: + let m = expandDefault(n1.typ, n1.info) + gen c, m, a + else: + var opB = c.genx(newSymNode(op)) + buildTyped c.code, info, Call, typeToIr(c.m.types, n.typ): + copyTree c.code, opB + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, typeToIr(c.m.types, n1.typ)): + copyTree c.code, a + +proc genDestroy(c: var ProcCon; n: PNode) = + let t = n[1].typ.skipTypes(abstractInst) + case t.kind + of tyString: + var unused = default(Value) + genUnaryCp(c, n, unused, "nimDestroyStrV1") + of tySequence: + #[ + var a = initLocExpr(c, arg) + linefmt(c, cpsStmts, "if ($1.p && ($1.p->cap & NIM_STRLIT_FLAG) == 0) {$n" & + " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & + "}$n", + [rdLoc(a), getTypeDesc(c.module, t.lastSon)]) + ]# + globalError(c.config, n.info, "not implemented: =destroy for seqs") + else: discard "nothing to do" + proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, d) @@ -1391,9 +1484,9 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: genArrayLen(c, n, d) - of mMulI: genBinaryOp(c, n, d, Mul) - of mDivI: genBinaryOp(c, n, d, Div) - of mModI: genBinaryOp(c, n, d, Mod) + of mMulI: genBinaryOp(c, n, d, CheckedMul) + of mDivI: genBinaryOp(c, n, d, CheckedDiv) + of mModI: genBinaryOp(c, n, d, CheckedMod) of mAddF64: genBinaryOp(c, n, d, Add) of mSubF64: genBinaryOp(c, n, d, Sub) of mMulF64: genBinaryOp(c, n, d, Mul) @@ -1495,7 +1588,6 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = localError(c.config, n.info, sizeOfLikeMsg("offsetof")) of mRunnableExamples: discard "just ignore any call to runnableExamples" - of mDestroy, mTrace: discard "ignore calls to the default destructor" of mOf: genOf(c, n, d) of mAppendStrStr: unused(c, n, d) @@ -1522,49 +1614,23 @@ proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = of mConStrStr: genStrConcat(c, n, d) of mDefault, mZeroDefault: genDefault c, n, d + of mMove: genMove(c, n, d) + of mWasMoved, mReset: + unused(c, n, d) + genWasMoved(c, n) + of mDestroy: genDestroy(c, n) + #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0") + #of mAccessTypeField: genAccessTypeField(c, n, d) + #of mSlice: genSlice(c, n, d) + of mTrace: discard "no code to generate" else: - # mGCref, mGCunref, + # mGCref, mGCunref: unused by ORC globalError(c.config, n.info, "cannot generate code for: " & $m) #[ - of mReset: - unused(c, n, d) - var d = c.genx(n[1]) - # XXX use ldNullOpcode() here? - c.gABx(n, opcLdNull, d, c.genType(n[1].typ)) - c.gABC(n, opcNodeToReg, d, d) - c.gABx(n, ldNullOpcode(n.typ), d, c.genType(n.typ)) - - of mConStrStr: genVarargsABC(c, n, d, opcConcatStr) - of mRepr: genUnaryABC(c, n, d, opcRepr) - of mSlice: - var - d = c.genx(n[1]) - left = c.genIndex(n[2], n[1].typ) - right = c.genIndex(n[3], n[1].typ) - if isEmpty(d): d = c.getTemp(n) - c.gABC(n, opcNodeToReg, d, d) - c.gABC(n, opcSlice, d, left, right) - c.freeTemp(left) - c.freeTemp(right) - c.freeTemp(d) - - of mMove: - let arg = n[1] - let a = c.genx(arg) - if isEmpty(d): d = c.getTemp(arg) - gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a) - c.freeTemp(a) - of mDup: - let arg = n[1] - let a = c.genx(arg) - if isEmpty(d): d = c.getTemp(arg) - gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), d, a) - c.freeTemp(a) - of mNodeId: c.genUnaryABC(n, d, opcNodeId) @@ -1764,6 +1830,63 @@ proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = valueIntoDest c, info, d, n.typ, body freeTemp c, tmp +proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) = + let arrType = typ.skipTypes(abstractVar) + let elemType = arrayPtrTypeOf(c.m.types.g, typeToIr(c.m.types, arrType.lastSon)) + case arrType.kind + of tyString: + let t = typeToIr(c.m.types, typ.lastSon) + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types): + copyTree target, tmp + target.addImmediateVal info, 1 # (len, p)-pair + target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + # len: + target.addImmediateVal info, 1 + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, tmp + target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + of tySequence: + let t = typeToIr(c.m.types, typ.lastSon) + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, typ): + copyTree target, tmp + target.addImmediateVal info, 1 # (len, p)-pair + target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + # len: + target.addImmediateVal info, 1 + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, tmp + target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + of tyArray: + let t = typeToIr(c.m.types, typ.lastSon) + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + copyTree target, tmp + target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + target.addImmediateVal info, 1 + target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) + else: + raiseAssert "addAddrOfFirstElem: " & typeToString(typ) + +proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlags; destType: PType) = + let info = toLineInfo(c, arg.info) + let tmp = c.genx(arg, flags) + let arrType = destType.skipTypes(abstractVar) + template body(target) = + buildTyped target, info, ObjConstr, typeToIr(c.m.types, arrType): + c.addAddrOfFirstElem target, info, tmp, arg.typ + + valueIntoDest c, info, d, arrType, body + freeTemp c, tmp + proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) = let targetType = n.typ.skipTypes({tyDistinct}) let argType = arg.typ.skipTypes({tyDistinct}) @@ -1774,6 +1897,11 @@ proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: gen c, arg, d return + if opc != Cast and targetType.skipTypes({tyVar, tyLent}).kind in {tyOpenArray, tyVarargs} and + argType.skipTypes({tyVar, tyLent}).kind notin {tyOpenArray, tyVarargs}: + genToOpenArrayConv c, arg, d, flags, n.typ + return + let info = toLineInfo(c, n.info) let tmp = c.genx(arg, flags) template body(target) = @@ -1850,7 +1978,7 @@ proc genVarSection(c: var ProcCon; n: PNode) = genAsgn2(c, vn, a[2]) else: if a[2].kind == nkEmpty: - discard "XXX assign default value to location here" + genAsgn2(c, vn, expandDefault(vn.typ, vn.info)) else: genAsgn2(c, vn, a[2]) @@ -1922,17 +2050,56 @@ proc genNilLit(c: var ProcCon; n: PNode; d: var Value) = valueIntoDest c, info, d, n.typ, body proc genRangeCheck(c: var ProcCon; n: PNode; d: var Value) = - # XXX to implement properly - gen c, n[0], d + if optRangeCheck in c.options: + let info = toLineInfo(c, n.info) + let tmp = c.genx n[0] + let a = c.genx n[1] + let b = c.genx n[2] + template body(target) = + buildTyped target, info, CheckedRange, typeToIr(c.m.types, n.typ): + copyTree target, tmp + copyTree target, a + copyTree target, b + target.addLabel info, CheckedGoto, c.exitLabel + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + freeTemp c, a + freeTemp c, b + else: + gen c, n[0], d + +type + IndexFor = enum + ForSeq, ForStr, ForOpenArray + +proc genIndexCheck(c: var ProcCon; n: PNode; a: Value; kind: IndexFor): Value = + if optBoundsCheck in c.options: + let info = toLineInfo(c, n.info) + result = default(Value) + let idx = genx(c, n) + build result, info, CheckedIndex: + copyTree result.Tree, idx + case kind + of ForSeq, ForStr: + buildTyped result, info, FieldAt, c.m.nativeIntId: + copyTree result.Tree, a + result.addImmediateVal info, 0 # (len, p)-pair + of ForOpenArray: + buildTyped result, info, FieldAt, c.m.nativeIntId: + copyTree result.Tree, a + result.addImmediateVal info, 1 # (p, len)-pair + result.Tree.addLabel info, CheckedGoto, c.exitLabel + freeTemp c, idx + else: + result = genx(c, n) proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = let arrayKind = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind let info = toLineInfo(c, n.info) case arrayKind of tyString: - # XXX implement range check let a = genx(c, n[0], flags) - let b = genx(c, n[1]) + let b = genIndexCheck(c, n[1], a, ForStr) let t = typeToIr(c.m.types, n.typ) template body(target) = buildTyped target, info, ArrayAt, t: @@ -1966,9 +2133,8 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = freeTemp c, a of tyOpenArray, tyVarargs: - # XXX implement range check let a = genx(c, n[0], flags) - let b = genx(c, n[1]) + let b = genIndexCheck(c, n[1], a, ForOpenArray) let t = typeToIr(c.m.types, n.typ) template body(target) = buildTyped target, info, ArrayAt, t: @@ -1981,7 +2147,6 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = freeTemp c, b freeTemp c, a of tyArray: - # XXX implement range check let a = genx(c, n[0], flags) var b = default(Value) genIndex(c, n[1], n[0].typ, b) @@ -1995,7 +2160,7 @@ proc genArrAccess(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = freeTemp c, a of tySequence: let a = genx(c, n[0], flags) - let b = genx(c, n[1]) + let b = genIndexCheck(c, n[1], a, ForSeq) let t = typeToIr(c.m.types, n.typ) template body(target) = buildTyped target, info, ArrayAt, t: @@ -2047,7 +2212,7 @@ proc genProc(cOuter: var ProcCon; n: PNode) = var c = initProcCon(cOuter.m, prc, cOuter.m.graph.config) genParams(c, prc.typ.n) - let body = transformBody(c.m.graph, c.m.idgen, prc, useCache) + let body = transformBody(c.m.graph, c.m.idgen, prc, {useCache, keepOpenArrayConversions}) let info = toLineInfo(c, body.info) build c.code, info, ProcDecl: diff --git a/compiler/nir/nirinsts.nim b/compiler/nir/nirinsts.nim index f037b4f0e0e8..2c0dc3d1143a 100644 --- a/compiler/nir/nirinsts.nim +++ b/compiler/nir/nirinsts.nim @@ -66,6 +66,9 @@ type SetExc, TestExc, + CheckedRange, + CheckedIndex, + Call, IndirectCall, CheckedCall, # call that can raise diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim index d989397a6080..a42feab005c8 100644 --- a/compiler/nir/nirtypes.nim +++ b/compiler/nir/nirtypes.nim @@ -239,6 +239,11 @@ proc ptrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = g.addType t result = sealType(g, f) +proc arrayPtrTypeOf*(g: var TypeGraph; t: TypeId): TypeId = + let f = g.openType AArrayPtrTy + g.addType t + result = sealType(g, f) + proc toString*(dest: var string; g: TypeGraph; i: TypeId) = case g[i].kind of VoidTy: dest.add "void" diff --git a/compiler/nir/types2ir.nim b/compiler/nir/types2ir.nim index 6d163c6c7c33..835bef03c8c8 100644 --- a/compiler/nir/types2ir.nim +++ b/compiler/nir/types2ir.nim @@ -459,7 +459,11 @@ proc typeToIr*(c: var TypesCon; t: PType): TypeId = let a = openType(c.g, LastArrayTy) c.g.addType(elemType) result = sealType(c.g, a) - of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc, + of tyUntyped, tyTyped: + # this avoids a special case for system.echo which is not a generic but + # uses `varargs[typed]`: + result = VoidId + of tyNone, tyEmpty, tyTypeDesc, tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index 56a0039babd0..7e46f3d0b5f4 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -66,7 +66,7 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} = proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool = result = false for i in 0..<branch.len: - if exprStructuralEquivalent(branch[i][1].skipAddr, moveTarget, + if exprStructuralEquivalent(branch[i][1].skipHiddenAddr, moveTarget, strictSymEquality = true): result = true matches.incl i @@ -76,7 +76,7 @@ proc intersect(summary: var seq[PNode]; branch: seq[PNode]) = var i = 0 var matches = initIntSet() while i < summary.len: - if wasMovedTarget(matches, branch, summary[i][1].skipAddr): + if wasMovedTarget(matches, branch, summary[i][1].skipHiddenAddr): inc i else: summary.del i @@ -87,7 +87,7 @@ proc intersect(summary: var seq[PNode]; branch: seq[PNode]) = proc invalidateWasMoved(c: var BasicBlock; x: PNode) = var i = 0 while i < c.wasMovedLocs.len: - if exprStructuralEquivalent(c.wasMovedLocs[i][1].skipAddr, x, + if exprStructuralEquivalent(c.wasMovedLocs[i][1].skipHiddenAddr, x, strictSymEquality = true): c.wasMovedLocs.del i else: @@ -96,7 +96,7 @@ proc invalidateWasMoved(c: var BasicBlock; x: PNode) = proc wasMovedDestroyPair(c: var Con; b: var BasicBlock; d: PNode) = var i = 0 while i < b.wasMovedLocs.len: - if exprStructuralEquivalent(b.wasMovedLocs[i][1].skipAddr, d[1].skipAddr, + if exprStructuralEquivalent(b.wasMovedLocs[i][1].skipHiddenAddr, d[1].skipHiddenAddr, strictSymEquality = true): b.wasMovedLocs[i].flags.incl nfMarkForDeletion c.somethingTodo = true diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f542a124476e..423cfbb3419a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -993,9 +993,9 @@ proc trackCall(tracked: PEffects; n: PNode) = # consider this case: p(out x, x); we want to remark that 'x' is not # initialized until after the call. Since we do this after we analysed the # call, this is fine. - initVar(tracked, n[i].skipAddr, false) + initVar(tracked, n[i].skipHiddenAddr, false) if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and - isDangerousLocation(n[i].skipAddr, tracked.owner): + isDangerousLocation(n[i].skipHiddenAddr, tracked.owner): if sfNoSideEffect in tracked.owner.flags: localError(tracked.config, n[i].info, "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i])) diff --git a/compiler/transf.nim b/compiler/transf.nim index 6430243d95d1..65b4c6c3bd00 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -28,10 +28,11 @@ when defined(nimPreviewSlimSystem): import std/assertions type - TransformBodyFlag* = enum - dontUseCache, useCache + TransformFlag* = enum + useCache, keepOpenArrayConversions, force + TransformFlags* = set[TransformFlag] -proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, flag: TransformBodyFlag, force = false): PNode +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode import closureiters, lambdalifting @@ -50,9 +51,10 @@ type module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) - isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool + isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) + flags: TransformFlags graph: ModuleGraph idgen: IdGenerator @@ -116,7 +118,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = let s = n.sym if s.typ != nil and s.typ.callConv == ccClosure: if s.kind in routineKinds: - discard transformBody(c.graph, c.idgen, s, useCache) + discard transformBody(c.graph, c.idgen, s, {useCache}+c.flags) if s.kind == skIterator: if c.tooEarly: return n else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c)) @@ -552,11 +554,14 @@ proc transformConv(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) of tyOpenArray, tyVarargs: - result = transform(c, n[1]) - #result = transformSons(c, n) - result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen) - #echo n.info, " came here and produced ", typeToString(result.typ), - # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) + if keepOpenArrayConversions in c.flags: + result = transformSons(c, n) + else: + result = transform(c, n[1]) + #result = transformSons(c, n) + result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen) + #echo n.info, " came here and produced ", typeToString(result.typ), + # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) of tyCstring: if source.kind == tyString: result = newTransNode(nkStringToCString, n, 1) @@ -774,7 +779,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true)) idNodeTablePut(newC.mapping, formal, temp) - let body = transformBody(c.graph, c.idgen, iter, useCache) + let body = transformBody(c.graph, c.idgen, iter, {useCache}+c.flags) pushInfoContext(c.graph.config, n.info) inc(c.inlining) stmtList.add(transform(c, body)) @@ -1137,13 +1142,8 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = popTransCon(c) incl(result.flags, nfTransf) -proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator): PTransf = - new(result) - result.contSyms = @[] - result.breakSyms = @[] - result.module = module - result.graph = g - result.idgen = idgen +proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator; flags: TransformFlags): PTransf = + result = PTransf(module: module, graph: g, idgen: idgen, flags: flags) proc flattenStmts(n: PNode) = var goOn = true @@ -1186,7 +1186,7 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: TransformBodyFlag, force = false): PNode = +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode = assert prc.kind in routineKinds if prc.transformedBody != nil: @@ -1195,8 +1195,8 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: Transfo result = getBody(g, prc) else: prc.transformedBody = newNode(nkEmpty) # protects from recursion - var c = openTransf(g, prc.getModule, "", idgen) - result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen, force) + var c = openTransf(g, prc.getModule, "", idgen, flags) + result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen, flags) result = processTransf(c, result, prc) liftDefer(c, result) result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen) @@ -1206,7 +1206,7 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: Transfo incl(result.flags, nfTransf) - if flag == useCache or prc.typ.callConv == ccInline: + if useCache in flags or prc.typ.callConv == ccInline: # genProc for inline procs will be called multiple times from different modules, # it is important to transform exactly once to get sym ids and locations right prc.transformedBody = result @@ -1217,21 +1217,21 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: Transfo #if prc.name.s == "main": # echo "transformed into ", renderTree(result, {renderIds}) -proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode = +proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(g, module, "", idgen) + var c = openTransf(g, module, "", idgen, flags) result = processTransf(c, n, module) liftDefer(c, result) #result = liftLambdasForTopLevel(module, result) incl(result.flags, nfTransf) -proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode = +proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(g, module, "", idgen) + var c = openTransf(g, module, "", idgen, flags) result = processTransf(c, n, module) liftDefer(c, result) # expressions are not to be injected with destructor calls as that diff --git a/compiler/trees.nim b/compiler/trees.nim index f038fbc1eb3c..e39cbafe6190 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -234,3 +234,6 @@ proc isRunnableExamples*(n: PNode): bool = # Templates and generics don't perform symbol lookups. result = n.kind == nkSym and n.sym.magic == mRunnableExamples or n.kind == nkIdent and n.ident.id == ord(wRunnableExamples) + +proc skipAddr*(n: PNode): PNode {.inline.} = + result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n diff --git a/compiler/vm.nim b/compiler/vm.nim index 85c1305e94eb..fd03c4baeb0b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1295,7 +1295,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = a.sym.ast.shallowCopy for i in 0..<a.sym.ast.len: ast[i] = a.sym.ast[i] - ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, useCache, force=true) + ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, {useCache, force}) ast.copyTree() of opcSymOwner: decodeB(rkNode) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 42ce09596895..efcd0ec35b04 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -2388,7 +2388,7 @@ proc genProc(c: PCtx; s: PSym): int = c.procToCodePos[s.id] = result # thanks to the jmp we can add top level statements easily and also nest # procs easily: - let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): dontUseCache else: useCache) + let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): {} else: {useCache}) let procStart = c.xjmp(body, opcJmp, 0) var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index e79a2b324c0d..5e4cda186193 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -206,6 +206,9 @@ proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} = prepareAdd(s, src.len) appendString s, src +proc nimDestroyStrV1(s: NimStringV2) {.compilerRtl, inl.} = + frees(s) + func capacity*(self: string): int {.inline.} = ## Returns the current capacity of the string. # See https://github.com/nim-lang/RFCs/issues/460 From 61145b1d4bd60712dbaeaca19d01f3696546046c Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:58:43 +0800 Subject: [PATCH 347/347] fixes #22354; Wrong C++ codegen for default parameter values in ORC (#22819) fixes #22354 It skips `nkHiddenAddr`. No need to hoist `var parameters` without side effects. Besides, it saves lots of temporary variables in ORC. --- compiler/semexprs.nim | 4 +- tests/ccgbugs2/tcodegen.nim | 75 +++++++++++++++++++++++-------------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8cdd16f02a74..e6983910de6d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2927,7 +2927,7 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym: let paramPos = defExpr.sym.position + 1 - if call[paramPos].kind != nkSym: + if call[paramPos].skipAddr.kind != nkSym: let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen, c.p.owner, letSection.info, c.p.owner.options) hoistedVarSym.typ = call[paramPos].typ @@ -2939,7 +2939,7 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym - # arg we refer to is a sym, wether introduced by hoisting or not doesn't matter, we simply reuse it + # arg we refer to is a sym, whether introduced by hoisting or not doesn't matter, we simply reuse it defExpr = call[paramPos] else: for i in 0..<defExpr.safeLen: diff --git a/tests/ccgbugs2/tcodegen.nim b/tests/ccgbugs2/tcodegen.nim index 84cd76e2fdf3..aac1ecaf39ee 100644 --- a/tests/ccgbugs2/tcodegen.nim +++ b/tests/ccgbugs2/tcodegen.nim @@ -1,28 +1,47 @@ -discard """ - targets: "c cpp" -""" - -# bug #19094 -type - X = object - filler: array[2048, int] - innerAddress: uint - -proc initX(): X = - result.innerAddress = cast[uint](result.addr) - -proc initXInPlace(x: var X) = - x.innerAddress = cast[uint](x.addr) - -block: # NRVO1 - var x = initX() - let innerAddress = x.innerAddress - let outerAddress = cast[uint](x.addr) - doAssert(innerAddress == outerAddress) # [OK] - -block: # NRVO2 - var x: X - initXInPlace(x) - let innerAddress = x.innerAddress - let outerAddress = cast[uint](x.addr) - doAssert(innerAddress == outerAddress) # [OK] +discard """ + targets: "c cpp" +""" + +# bug #19094 +type + X = object + filler: array[2048, int] + innerAddress: uint + +proc initX(): X = + result.innerAddress = cast[uint](result.addr) + +proc initXInPlace(x: var X) = + x.innerAddress = cast[uint](x.addr) + +block: # NRVO1 + var x = initX() + let innerAddress = x.innerAddress + let outerAddress = cast[uint](x.addr) + doAssert(innerAddress == outerAddress) # [OK] + +block: # NRVO2 + var x: X + initXInPlace(x) + let innerAddress = x.innerAddress + let outerAddress = cast[uint](x.addr) + doAssert(innerAddress == outerAddress) # [OK] + +block: # bug #22354 + type Object = object + foo: int + + proc takeFoo(self: var Object): int = + result = self.foo + self.foo = 999 + + proc doSomething(self: var Object; foo: int = self.takeFoo()) = + discard + + proc main() = + var obj = Object(foo: 2) + obj.doSomething() + doAssert obj.foo == 999 + + + main()