From 248caaf27eb67556aea23c3ceff204237d4df9c3 Mon Sep 17 00:00:00 2001 From: superfunc Date: Fri, 15 Sep 2017 12:49:00 -0700 Subject: [PATCH 01/56] Update docs on assignment operator (#6373) --- doc/manual/types.txt | 3 +-- doc/tut1.rst | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/manual/types.txt b/doc/manual/types.txt index e6875f2df0..2c4b019ad1 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -570,8 +570,7 @@ order. The *names* of the fields also have to be identical. The assignment operator for tuples copies each component. The default assignment operator for objects copies each component. Overloading -of the assignment operator for objects is not possible, but this will change -in future versions of the compiler. +of the assignment operator is described in `type-bound-operations-operator`_. .. code-block:: nim diff --git a/doc/tut1.rst b/doc/tut1.rst index 89893a39a9..be5cd8c115 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -185,9 +185,8 @@ to a storage location: var x = "abc" # introduces a new variable `x` and assigns a value to it x = "xyz" # assigns a new value to `x` -``=`` is the *assignment operator*. The assignment operator cannot be -overloaded, overwritten or forbidden, but this might change in a future version -of Nim. You can declare multiple variables with a single assignment +``=`` is the *assignment operator*. The assignment operator can be +overloaded. You can declare multiple variables with a single assignment statement and all the variables will have the same value: .. code-block:: From 662f043b59a691fdd7d2af3d69af9ed6ddbbb428 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 15 Sep 2017 22:18:39 +0200 Subject: [PATCH 02/56] development version is odd --- lib/system.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 157f323876..bd354b675d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1909,7 +1909,7 @@ const NimMinor*: int = 17 ## is the minor number of Nim's version. - NimPatch*: int = 2 + NimPatch*: int = 3 ## is the patch number of Nim's version. NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch @@ -3402,10 +3402,10 @@ when hasAlloc or defined(nimscript): proc `[]`*(s: string, x: Slice[int]): string {.inline.} = ## slice operation for strings. ## returns the inclusive range [s[x.a], s[x.b]]: - ## + ## ## .. code-block:: nim ## var s = "abcdef" - ## assert s[1..3] == "bcd" + ## assert s[1..3] == "bcd" result = s.substr(x.a, x.b) proc `[]=`*(s: var string, x: Slice[int], b: string) = @@ -3427,7 +3427,7 @@ when hasAlloc or defined(nimscript): proc `[]`*[Idx, T](a: array[Idx, T], x: Slice[int]): seq[T] = ## slice operation for arrays. ## returns the inclusive range [a[x.a], a[x.b]]: - ## + ## ## .. code-block:: nim ## var a = [1,2,3,4] ## assert a[0..2] == @[1,2,3] @@ -3466,7 +3466,7 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: Slice[Idx], b: openArray[T]) = proc `[]`*[T](s: seq[T], x: Slice[int]): seq[T] = ## slice operation for sequences. ## returns the inclusive range [s[x.a], s[x.b]]: - ## + ## ## .. code-block:: nim ## var s = @[1,2,3,4] ## assert s[0..2] == @[1,2,3] From 9258672cee32ca82fcf1494447cd9020602df567 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 16 Sep 2017 01:02:59 -0500 Subject: [PATCH 03/56] balance Genode CPU pinning, deadlock at Genode exit (#6317) * Genode: balance thread CPU affinities Genode threads are pinned by defaut to the same CPU as the initial component entrypoint thread. Thread affinities are also permanent. This patch pins new threads to CPUs in a round-robin manner. Arbitrary CPU pinning is not exposed and the 'nimPinToCpu' has no effect. * Genode: guarantee that 'quit' will not return On Genode exits are handled by whatever component is acting as parent. The caller has no guarentee that the parent implementation will halt the caller's threads, so explicitly deadlock the 'quit' procedure. --- lib/genode_cpp/threads.h | 12 ++++++++---- lib/system.nim | 3 ++- lib/system/threads.nim | 9 +++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/genode_cpp/threads.h b/lib/genode_cpp/threads.h index 043f808f10..a7cb2f17b5 100644 --- a/lib/genode_cpp/threads.h +++ b/lib/genode_cpp/threads.h @@ -31,8 +31,12 @@ struct Nim::SysThread void entry() override { (_func)(_arg); } - Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg) - : Genode::Thread(env, "nim-thread", stack_size), _func(func), _arg(arg) + Thread(Genode::Env &env, Genode::size_t stack_size, Entry func, void *arg, int affinity) + : Genode::Thread(env, "nim-thread", stack_size, + env.cpu().affinity_space().location_of_index(affinity), + Genode::Cpu_session::Weight(Genode::Cpu_session::Weight::DEFAULT_WEIGHT-1), + env.cpu()), + _func(func), _arg(arg) { Genode::Thread::start(); } @@ -40,8 +44,8 @@ struct Nim::SysThread Genode::Constructible _thread; - void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg) { - _thread.construct(*env, stack_size, func, arg); } + void initThread(Genode::Env *env, Genode::size_t stack_size, Entry func, void *arg, int aff) { + _thread.construct(*env, stack_size, func, arg, aff); } void joinThread() { _thread->join(); } diff --git a/lib/system.nim b/lib/system.nim index 157f323876..39f65c5a30 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1435,7 +1435,8 @@ when defined(nimdoc): elif defined(genode): proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn, - importcpp: "genodeEnv->parent().exit(@)", header: "".} + importcpp: "genodeEnv->parent().exit(@); Genode::sleep_forever()", + header: "".} else: proc quit*(errorcode: int = QuitSuccess) {. diff --git a/lib/system/threads.nim b/lib/system/threads.nim index a7a8118448..96c045e6b9 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -127,7 +127,8 @@ elif defined(genode): proc initThread(s: var SysThread, stackSize: culonglong, entry: GenodeThreadProc, - arg: pointer) {. + arg: pointer, + affinity: cuint) {. importcpp: "#.initThread(genodeEnv, @)".} proc threadVarAlloc(): ThreadVarSlot = 0 @@ -567,6 +568,9 @@ when hostOS == "windows": setThreadAffinityMask(t.sys, uint(1 shl cpu)) elif defined(genode): + var affinityOffset: cuint = 1 + # CPU affinity offset for next thread, safe to roll-over + proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, param: TArg) = @@ -577,7 +581,8 @@ elif defined(genode): when hasSharedHeap: t.stackSize = ThreadStackSize t.sys.initThread( ThreadStackSize.culonglong, - threadProcWrapper[TArg], addr(t)) + threadProcWrapper[TArg], addr(t), affinityOffset) + inc affinityOffset proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = {.hint: "cannot change Genode thread CPU affinity after initialization".} From 2b592a64259e4f66b0ad5dd7264f7b524c309fee Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Sep 2017 10:29:29 +0200 Subject: [PATCH 04/56] in prepration for the upcoming different integer inference rules --- lib/core/macros.nim | 2 +- lib/pure/strutils.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3cfefb5c1c..e88002c7b9 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -669,7 +669,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = t = x result = newString(len) for j in countdown(len-1, 0): - result[j] = HexChars[t and 0xF] + result[j] = HexChars[int(t and 0xF)] t = t shr 4 # handle negative overflow if t == 0 and x < 0: t = -1 diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 2a64e70d96..8b4e6bd05c 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -888,7 +888,7 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, n = x result = newString(len) for j in countdown(len-1, 0): - result[j] = HexChars[n and 0xF] + result[j] = HexChars[int(n and 0xF)] n = n shr 4 # handle negative overflow if n == 0 and x < 0: n = -1 From e7bcadaf3260b34e7c8b313549b93130f3ea79e4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Sep 2017 10:56:28 +0200 Subject: [PATCH 05/56] hotfix: fixes seq.add and str.add for edge cases --- lib/system/sysstr.nim | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index c3150cb078..9ba53a5b87 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -140,9 +140,10 @@ proc addChar(s: NimString, c: char): NimString = # is compilerproc! result = s if result.len >= result.space: - result.reserved = resize(result.space) + let r = resize(result.space) result = cast[NimString](growObj(result, - sizeof(TGenericSeq) + result.reserved + 1)) + sizeof(TGenericSeq) + r + 1)) + result.reserved = r result.data[result.len] = c result.data[result.len+1] = '\0' inc(result.len) @@ -218,26 +219,29 @@ proc incrSeq(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = # seq[seq->len-1] = x; result = seq if result.len >= result.space: - result.reserved = resize(result.space) - result = cast[PGenericSeq](growObj(result, elemSize * result.reserved + + let r = resize(result.space) + result = cast[PGenericSeq](growObj(result, elemSize * r + GenericSeqSize)) + result.reserved = r inc(result.len) proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} = # incrSeq version 2 result = seq if result.len >= result.space: - result.reserved = resize(result.space) - result = cast[PGenericSeq](growObj(result, elemSize * result.reserved + + let r = resize(result.space) + result = cast[PGenericSeq](growObj(result, elemSize * r + GenericSeqSize)) + result.reserved = r proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {. compilerRtl.} = result = seq if result.space < newLen: - result.reserved = max(resize(result.space), newLen) - result = cast[PGenericSeq](growObj(result, elemSize * result.reserved + + let r = max(resize(result.space), newLen) + result = cast[PGenericSeq](growObj(result, elemSize * r + GenericSeqSize)) + result.reserved = r elif newLen < result.len: # we need to decref here, otherwise the GC leaks! when not defined(boehmGC) and not defined(nogc) and From 12af4a3f8ac290f5c6f7d208f1a1951a141449ff Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 Sep 2017 10:57:21 +0200 Subject: [PATCH 06/56] prepared stdlb for new integer arithmetic rules --- lib/pure/md5.nim | 12 ++++++------ lib/pure/securehash.nim | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 44b9ed0d4e..1ff3a9824c 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -78,10 +78,10 @@ proc encode(dest: var MD5Block, src: cstring) = proc decode(dest: var openArray[uint8], src: openArray[uint32]) = var i = 0 for j in 0..high(src): - dest[i] = src[j] and 0xff'u32 - dest[i+1] = src[j] shr 8 and 0xff'u32 - dest[i+2] = src[j] shr 16 and 0xff'u32 - dest[i+3] = src[j] shr 24 and 0xff'u32 + dest[i] = uint8(src[j] and 0xff'u32) + dest[i+1] = uint8(src[j] shr 8 and 0xff'u32) + dest[i+2] = uint8(src[j] shr 16 and 0xff'u32) + dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) inc(i, 4) proc transform(buffer: pointer, state: var MD5State) = @@ -216,8 +216,8 @@ proc `$`*(d: MD5Digest): string = const digits = "0123456789abcdef" result = "" for i in 0..15: - add(result, digits[(d[i] shr 4) and 0xF]) - add(result, digits[d[i] and 0xF]) + add(result, digits[(d[i].int shr 4) and 0xF]) + add(result, digits[d[i].int and 0xF]) proc getMD5*(s: string): string = ## computes an MD5 value of `s` and returns its string representation diff --git a/lib/pure/securehash.nim b/lib/pure/securehash.nim index f141732a70..c191466698 100644 --- a/lib/pure/securehash.nim +++ b/lib/pure/securehash.nim @@ -148,13 +148,13 @@ proc sha1(src: cstring; len: int): Sha1Digest = while lastBlockBytes < endCurrentBlock: var value = uint32(src[lastBlockBytes + currentBlock]) shl - ((3'u32 - (lastBlockBytes and 3)) shl 3) + ((3'u32 - uint32(lastBlockBytes and 3)) shl 3) w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value inc(lastBlockBytes) w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or ( - 0x80'u32 shl ((3'u32 - (lastBlockBytes and 3)) shl 3) + 0x80'u32 shl ((3'u32 - uint32(lastBlockBytes and 3)) shl 3) ) if endCurrentBlock >= 56: From 5b8f33a9058b78e2e714ce341107ac52d98dcc5f Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Sat, 16 Sep 2017 20:09:44 +0200 Subject: [PATCH 07/56] Reorder json `add` and `%`, fixes #6385 (#6388) * Reorder json `add` and `%`, fixes https://github.com/nim-lang/Nim/issues/6385 * rename json test files --- lib/pure/json.nim | 20 ++++++++++---------- tests/stdlib/mjsonexternproc.nim | 10 ++++++++++ tests/stdlib/tjsonexternproc.nim | 5 +++++ 3 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 tests/stdlib/mjsonexternproc.nim create mode 100644 tests/stdlib/tjsonexternproc.nim diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 097952588d..73c4f97f45 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -721,6 +721,16 @@ proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = if n.isNil or n.kind != JArray: return default else: return n.elems +proc add*(father, child: JsonNode) = + ## Adds `child` to a JArray node `father`. + assert father.kind == JArray + father.elems.add(child) + +proc add*(obj: JsonNode, key: string, val: JsonNode) = + ## Sets a field from a `JObject`. + assert obj.kind == JObject + obj.fields[key] = val + proc `%`*(s: string): JsonNode = ## Generic constructor for JSON data. Creates a new `JString JsonNode`. new(result) @@ -909,16 +919,6 @@ proc contains*(node: JsonNode, val: JsonNode): bool = proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key) ## Deprecated for `hasKey` -proc add*(father, child: JsonNode) = - ## Adds `child` to a JArray node `father`. - assert father.kind == JArray - father.elems.add(child) - -proc add*(obj: JsonNode, key: string, val: JsonNode) = - ## Sets a field from a `JObject`. - assert obj.kind == JObject - obj.fields[key] = val - proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = ## Sets a field from a `JObject`. assert(obj.kind == JObject) diff --git a/tests/stdlib/mjsonexternproc.nim b/tests/stdlib/mjsonexternproc.nim new file mode 100644 index 0000000000..2967a1168c --- /dev/null +++ b/tests/stdlib/mjsonexternproc.nim @@ -0,0 +1,10 @@ +# Test case for https://github.com/nim-lang/Nim/issues/6385 + +import json +# export json + +proc foo*[T](a: T) = + let params = %*{ + "data": [ 1 ] + } + echo $params \ No newline at end of file diff --git a/tests/stdlib/tjsonexternproc.nim b/tests/stdlib/tjsonexternproc.nim new file mode 100644 index 0000000000..ec90e580d0 --- /dev/null +++ b/tests/stdlib/tjsonexternproc.nim @@ -0,0 +1,5 @@ +# Test case for https://github.com/nim-lang/Nim/issues/6385 + +import mjsonexternproc +# import json +foo(1) \ No newline at end of file From ae7fe5087f3e4f6ddc572254ae3ddcc8324099f1 Mon Sep 17 00:00:00 2001 From: Daniil Yarancev <21169548+Yardanico@users.noreply.github.com> Date: Sat, 16 Sep 2017 23:54:32 +0300 Subject: [PATCH 08/56] Closes #6083, closes #6086, closes #6379 (#6392) --- tests/vm/tvmmisc.nim | 66 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index b7112b0999..42a58fa8fd 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -1,16 +1,66 @@ - - # bug #4462 import macros -proc foo(t: typedesc) {.compileTime.} = - echo getType(t).treeRepr +block: + proc foo(t: typedesc) {.compileTime.} = + assert sameType(getType(t), getType(int)) -static: - foo(int) + static: + foo(int) # #4412 -proc default[T](t: typedesc[T]): T {.inline.} = discard +block: + proc default[T](t: typedesc[T]): T {.inline.} = discard + static: + var x = default(type(0)) + +# #6379 static: - var x = default(type(0)) + import algorithm + + var numArray = [1, 2, 3, 4, -1] + numArray.sort(cmp) + assert numArray == [-1, 1, 2, 3, 4] + + var str = "cba" + str.sort(cmp) + assert str == "abc" + +# #6086 +import math, sequtils, future + +block: + proc f: int = + toSeq(10..<10_000).filter( + a => a == ($a).map( + d => (d.ord-'0'.ord).int^4 + ).sum + ).sum + + var a = f() + const b = f() + + assert a == b + +block: + proc f(): seq[char] = + result = "hello".map(proc(x: char): char = x) + + var runTime = f() + const compTime = f() + assert runTime == compTime + +# #6083 +block: + proc abc(): seq[int] = + result = @[0] + result.setLen(2) + var tmp: int + + for i in 0 .. <2: + inc tmp + result[i] = tmp + + const fact1000 = abc() + assert fact1000 == @[1, 2] From ea47234b3560d779f881cf118c8d36800cc72d0a Mon Sep 17 00:00:00 2001 From: ephja Date: Sun, 17 Sep 2017 15:48:02 +0200 Subject: [PATCH 09/56] Array typedesc len (#6032) --- lib/system.nim | 2 +- tests/array/tarraylen.nim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/array/tarraylen.nim diff --git a/lib/system.nim b/lib/system.nim index a4460570af..efffeaeca1 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -718,7 +718,7 @@ proc len*[TOpenArray: openArray|varargs](x: TOpenArray): int {. magic: "LengthOpenArray", noSideEffect.} proc len*(x: string): int {.magic: "LengthStr", noSideEffect.} proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} -proc len*[I, T](x: array[I, T]): int {.magic: "LengthArray", noSideEffect.} +proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.} proc len*[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.} ## returns the length of an array, an openarray, a sequence or a string. ## This is roughly the same as ``high(T)-low(T)+1``, but its resulting type is diff --git a/tests/array/tarraylen.nim b/tests/array/tarraylen.nim new file mode 100644 index 0000000000..e9612de58b --- /dev/null +++ b/tests/array/tarraylen.nim @@ -0,0 +1,18 @@ +discard """ + output: "" +""" +var a: array[0, int] +doAssert a.len == 0 +doAssert array[0..0, int].len == 1 +doAssert array[0..0, int]([1]).len == 1 +doAssert array[1..1, int].len == 1 +doAssert array[1..1, int]([1]).len == 1 +doAssert array[2, int].len == 2 +doAssert array[2, int]([1, 2]).len == 2 +doAssert array[1..3, int].len == 3 +doAssert array[1..3, int]([1, 2, 3]).len == 3 +doAssert array[0..2, int].len == 3 +doAssert array[0..2, int]([1, 2, 3]).len == 3 +doAssert array[-2 .. -2, int].len == 1 +doAssert([1, 2, 3].len == 3) +doAssert([42].len == 1) \ No newline at end of file From fd4ef6ae8fea617af1ae5accdfff173b1990f263 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Sep 2017 17:36:37 +0200 Subject: [PATCH 10/56] .pure enums are much more convenient to use now --- compiler/importer.nim | 12 ++++++++++-- compiler/lookups.nim | 4 +++- compiler/semdata.nim | 4 +++- compiler/semexprs.nim | 6 ++++-- compiler/semgnrc.nim | 4 ++++ compiler/semtypes.nim | 4 +++- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/compiler/importer.nim b/compiler/importer.nim index c4861df7f4..dfc9e84ba6 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -60,6 +60,11 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 = else: result = fullPath.fileInfoIdx +proc importPureEnumField*(c: PContext; s: PSym) = + var check = strTableGet(c.importTable.symbols, s.name) + if check == nil: + strTableAdd(c.pureEnumFields, s) + proc rawImportSymbol(c: PContext, s: PSym) = # This does not handle stubs, because otherwise loading on demand would be # pointless in practice. So importing stubs is fine here! @@ -75,7 +80,7 @@ proc rawImportSymbol(c: PContext, s: PSym) = strTableAdd(c.importTable.symbols, s) if s.kind == skType: var etyp = s.typ - if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: + if etyp.kind in {tyBool, tyEnum}: for j in countup(0, sonsLen(etyp.n) - 1): var e = etyp.n.sons[j].sym if e.kind != skEnumField: @@ -91,7 +96,10 @@ proc rawImportSymbol(c: PContext, s: PSym) = break check = nextIdentIter(it, c.importTable.symbols) if e != nil: - rawImportSymbol(c, e) + if sfPure notin s.flags: + rawImportSymbol(c, e) + else: + importPureEnumField(c, e) else: # rodgen assures that converters and patterns are no stubs if s.kind == skConverter: addConverter(c, s) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 5c326d10a1..5a5dfc46a5 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -286,7 +286,7 @@ proc lookUp*(c: PContext, n: PNode): PSym = type TLookupFlag* = enum - checkAmbiguity, checkUndeclared, checkModule + checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage} @@ -297,6 +297,8 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = searchInScopes(c, ident).skipAlias(n) else: result = searchInScopes(c, ident, allExceptModule).skipAlias(n) + if result == nil and checkPureEnumFields in flags: + result = strTableGet(c.pureEnumFields, ident) if result == nil and checkUndeclared in flags: fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d422646a85..97212d2cd7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -65,7 +65,7 @@ type efWantStmt, efAllowStmt, efDetermineType, efExplain, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo, - + TExprFlags* = set[TExprFlag] TTypeAttachedOp* = enum @@ -112,6 +112,7 @@ type semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym includedFiles*: IntSet # used to detect recursive include files + pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously userPragmas*: TStrTable evalContext*: PEvalContext unknownIdents*: IntSet # ids of all unknown identifiers to prevent @@ -210,6 +211,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext result.converters = @[] result.patterns = @[] result.includedFiles = initIntSet() + initStrTable(result.pureEnumFields) initStrTable(result.userPragmas) result.generics = @[] result.unknownIdents = initIntSet() diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 65557658aa..9930e127e6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2115,8 +2115,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if nfSem in n.flags: return case n.kind of nkIdent, nkAccQuoted: - let checks = if efNoEvaluateGeneric in flags: {checkUndeclared} - else: {checkUndeclared, checkModule, checkAmbiguity} + let checks = if efNoEvaluateGeneric in flags: + {checkUndeclared, checkPureEnumFields} + else: + {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) result = semSym(c, n, s, flags) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 7e55b266a8..e3d078432d 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -105,6 +105,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, result = n let ident = considerQuotedIdent(n) var s = searchInScopes(c, ident).skipAlias(n) + if s == nil: + s = strTableGet(c.pureEnumFields, ident) + if s != nil and contains(c.ambiguousSymbols, s.id): + s = nil if s == nil: if ident.id notin ctx.toMixin and withinMixin notin flags: errorUndeclaredIdentifier(c, n.info, ident.s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a7c9244cc1..14a6d9ed57 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -88,7 +88,9 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if not isPure: strTableAdd(c.module.tab, e) addSon(result.n, newSymNode(e)) styleCheckDef(e) - if sfGenSym notin e.flags and not isPure: addDecl(c, e) + if sfGenSym notin e.flags: + if not isPure: addDecl(c, e) + else: importPureEnumField(c, e) if isPure and strTableIncl(symbols, e): wrongRedefinition(e.info, e.name.s) inc(counter) From cc24b6d4cb5ff1a810d6a2806aa20ad399f15c12 Mon Sep 17 00:00:00 2001 From: GULPF Date: Wed, 20 Sep 2017 13:44:44 +0200 Subject: [PATCH 11/56] Sets enhancements, fixes #2467 (#6158) --- lib/pure/collections/sets.nim | 98 +++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index d51a5c3880..dbdf17514f 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -287,8 +287,6 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} = if i >= 0: result = false - s.data[i].hcode = 0 - s.data[i].key = default(type(s.data[i].key)) dec(s.counter) while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 var j = i # The correctness of this depends on (h+1) in nextTry, @@ -300,7 +298,7 @@ proc exclImpl[A](s: var HashSet[A], key: A) : bool {. inline .} = if isEmpty(s.data[i].hcode): # end of collision cluster; So all done return r = s.data[i].hcode and msk # "home" location of key@i - shallowCopy(s.data[j], s.data[i]) # data[j] will be marked EMPTY next loop + shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop proc missingOrExcl*[A](s: var HashSet[A], key: A): bool = ## Excludes `key` in the set `s` and tells if `key` was removed from `s`. @@ -662,9 +660,12 @@ proc card*[A](s: OrderedSet[A]): int {.inline.} = template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} = var h = s.first + var idx = 0 while h >= 0: var nxt = s.data[h].next - if isFilled(s.data[h].hcode): yieldStmt + if isFilled(s.data[h].hcode): + yieldStmt + inc(idx) h = nxt iterator items*[A](s: OrderedSet[A]): A = @@ -689,6 +690,11 @@ iterator items*[A](s: OrderedSet[A]): A = forAllOrderedPairs: yield s.data[h].key +iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = + assert s.isValid, "The set needs to be initialized" + forAllOrderedPairs: + yield (idx, s.data[h].key) + proc rawGetKnownHC[A](s: OrderedSet[A], key: A, hc: Hash): int {.inline.} = rawGetKnownHCImpl() @@ -760,6 +766,67 @@ proc incl*[A](s: var HashSet[A], other: OrderedSet[A]) = assert other.isValid, "The set `other` needs to be initialized." for item in other: incl(s, item) +proc exclImpl[A](s: var OrderedSet[A], key: A) : bool {. inline .} = + assert s.isValid, "The set needs to be initialized." + var hc: Hash + var i = rawGet(s, key, hc) + var msk = high(s.data) + result = true + + if i >= 0: + result = false + # Fix ordering + if s.first == i: + s.first = s.data[i].next + else: + var itr = s.first + while true: + if (s.data[itr].next == i): + s.data[itr].next = s.data[i].next + if s.last == i: + s.last = itr + break + itr = s.data[itr].next + + dec(s.counter) + while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 + var j = i # The correctness of this depends on (h+1) in nextTry, + var r = j # though may be adaptable to other simple sequences. + s.data[i].hcode = 0 # mark current EMPTY + s.data[i].key = default(type(s.data[i].key)) + s.data[i].next = 0 + doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): + i = (i + 1) and msk # increment mod table size + if isEmpty(s.data[i].hcode): # end of collision cluster; So all done + return + r = s.data[i].hcode and msk # "home" location of key@i + shallowCopy(s.data[j], s.data[i]) # data[i] will be marked EMPTY next loop + +proc missingOrExcl*[A](s: var OrderedSet[A], key: A): bool = + ## Excludes `key` in the set `s` and tells if `key` was removed from `s`. Efficiency: O(n). + ## + ## The difference with regards to the `excl() <#excl,TOrderedSet[A],A>`_ proc is + ## that this proc returns `true` if `key` was not present in `s`. Example: + ## + ## .. code-block:: + ## var s = toOrderedSet([2, 3, 6, 7]) + ## assert s.missingOrExcl(4) == true + ## assert s.missingOrExcl(6) == false + exclImpl(s, key) + + +proc excl*[A](s: var OrderedSet[A], key: A) = + ## Excludes `key` from the set `s`. Efficiency: O(n). + ## + ## This doesn't do anything if `key` is not found in `s`. Example: + ## + ## .. code-block:: + ## var s = toOrderedSet([2, 3, 6, 7]) + ## s.excl(2) + ## s.excl(2) + ## assert s.len == 3 + discard exclImpl(s, key) + proc containsOrIncl*[A](s: var OrderedSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was added to `s`. ## @@ -986,6 +1053,24 @@ when isMainModule and not defined(release): assert a.len == b.card assert a.len == 2 + block setPairsIterator: + var s = toOrderedSet([1, 3, 5, 7]) + var items = newSeq[tuple[a: int, b: int]]() + for idx, item in s: items.add((idx, item)) + assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)] + + block exclusions: + var s = toOrderedSet([1, 2, 3, 6, 7, 4]) + + s.excl(3) + s.excl(3) + s.excl(1) + s.excl(4) + + var items = newSeq[int]() + for item in s: items.add item + assert items == @[2, 6, 7] + #block orderedSetIterator: # var a = initOrderedSet[int]() # for value in [9, 2, 1, 5, 1, 8, 4, 2]: @@ -1030,6 +1115,11 @@ when isMainModule and not defined(release): if s <= i or mustRehash(s, i): echo "performance issue: rightSize() will not elide enlarge() at ", i + block missingOrExcl: + var s = toOrderedSet([2, 3, 6, 7]) + assert s.missingOrExcl(4) == true + assert s.missingOrExcl(6) == false + when not defined(testing): echo "Micro tests run successfully." From dabe4aed8663db0b2e242df1d2945fd9dd6abee0 Mon Sep 17 00:00:00 2001 From: jlp765 Date: Thu, 21 Sep 2017 17:48:49 +1000 Subject: [PATCH 12/56] documentation improvement; make mixin easier to search (#6409) --- doc/manual/generics.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt index 87fcb78285..cceea33c0d 100644 --- a/doc/manual/generics.txt +++ b/doc/manual/generics.txt @@ -633,6 +633,9 @@ the ``vtptr`` magic produced types bound to ``ptr`` types. Symbol lookup in generics ------------------------- +Open and Closed symbols +~~~~~~~~~~~~~~~~~~~~~~~ + The symbol binding rules in generics are slightly subtle: There are "open" and "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation context, an "open" symbol can. Per default overloaded symbols are open @@ -658,6 +661,9 @@ the ``Index`` type is defined *after* the ``==`` for tuples; yet the example compiles as the instantiation takes the currently defined symbols into account too. +Mixin statement +--------------- + A symbol can be forced to be open by a `mixin`:idx: declaration: .. code-block:: nim From 92d67262fb4969d6ecd2f122c9ee14155abf75c3 Mon Sep 17 00:00:00 2001 From: wt Date: Thu, 21 Sep 2017 16:22:34 +0800 Subject: [PATCH 13/56] Fix `getPeerAddr()` `getPeerAddr()`: +`sin6_addr` (#6410) --- lib/pure/nativesockets.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 1a62c0bf6e..98fc62a3b9 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -498,7 +498,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = # Cannot use INET6_ADDRSTRLEN here, because it's a C define. var buf: array[64, char] if inet_ntop(name.sin6_family.cint, - addr name, buf.cstring, sizeof(buf).int32).isNil: + addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil: raiseOSError(osLastError()) result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) else: @@ -534,7 +534,7 @@ proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = # Cannot use INET6_ADDRSTRLEN here, because it's a C define. var buf: array[64, char] if inet_ntop(name.sin6_family.cint, - addr name, buf.cstring, sizeof(buf).int32).isNil: + addr name.sin6_addr, buf.cstring, sizeof(buf).int32).isNil: raiseOSError(osLastError()) result = ($buf, Port(nativesockets.ntohs(name.sin6_port))) else: From a04c7d8b38fd0027c2562dbd1401bf6ee7b909e4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 Sep 2017 11:38:04 +0200 Subject: [PATCH 14/56] introduce OnHeapNew and rename TLoc.s to TLoc.storage --- compiler/ast.nim | 7 +- compiler/ccgexprs.nim | 151 ++++++++++++++++++++-------------------- compiler/ccgstmts.nim | 2 +- compiler/ccgtypes.nim | 6 +- compiler/cgen.nim | 12 ++-- compiler/rodread.nim | 4 +- compiler/rodwrite.nim | 4 +- lib/pure/asyncmacro.nim | 2 +- 8 files changed, 96 insertions(+), 92 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 92abbb0ab0..2b1871bb8c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -747,12 +747,13 @@ type OnUnknown, # location is unknown (stack, heap or static) OnStatic, # in a static section OnStack, # location is on hardware stack - OnHeap # location is on heap or global + OnHeap, # location is on heap or global # (reference counting needed) + OnHeapNew # location is on the heap and was just new()'ed TLocFlags* = set[TLocFlag] TLoc* = object k*: TLocKind # kind of location - s*: TStorageLoc + storage*: TStorageLoc flags*: TLocFlags # location's flags t*: PType # type of location r*: Rope # rope value of location (code generators) @@ -1261,7 +1262,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(a.k): a.k = b.k - if a.s == low(a.s): a.s = b.s + if a.storage == low(a.storage): a.storage = b.storage a.flags = a.flags + b.flags if a.t == nil: a.t = b.t if a.r == nil: a.r = b.r diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f5c793d291..1064bf998f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -158,9 +158,9 @@ proc getStorageLoc(n: PNode): TStorageLoc = else: result = OnUnknown proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = - if dest.s == OnStack or not usesNativeGC(): + if dest.storage == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) - elif dest.s == OnHeap: + elif dest.storage in {OnHeap, OnHeapNew}: # location is on heap # now the writer barrier is inlined for performance: # @@ -202,13 +202,13 @@ proc asgnComplexity(n: PNode): int = proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != nil result.k = locField - result.s = a.s + result.storage = if a.storage == OnHeapNew: OnHeap else: a.storage result.t = t result.r = rdLoc(a) & "." & field proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = let newflags = - if src.s == OnStatic: + if src.storage == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} @@ -225,7 +225,7 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags, t: PNode, typ: PType) = if t == nil: return let newflags = - if src.s == OnStatic: + if src.storage == OnStatic: flags + {needToCopy} elif tfShallow in dest.t.flags: flags - {needToCopy} @@ -250,7 +250,7 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # (for objects, etc.): if needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: - if dest.s == OnStack or not usesNativeGC(): + if dest.storage == OnStack or not usesNativeGC(): useStringh(p.module) linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", @@ -274,18 +274,18 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if needToCopy notin flags and src.s != OnStatic: + if (needToCopy notin flags and src.storage != OnStatic) or src.storage == OnHeapNew: genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) of tyString: - if needToCopy notin flags and src.s != OnStatic: + if (needToCopy notin flags and src.storage != OnStatic) or src.storage == OnHeapNew: genRefAssign(p, dest, src, flags) else: - if dest.s == OnStack or not usesNativeGC(): + if dest.storage == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) - elif dest.s == OnHeap: + elif dest.storage in {OnHeap, OnHeapNew}: # we use a temporary to care for the dreaded self assignment: var tmp: TLoc getTemp(p, ty, tmp) @@ -357,7 +357,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) else: internalError("genAssignment: " & $ty.kind) - if optMemTracker in p.options and dest.s in {OnHeap, OnUnknown}: + if optMemTracker in p.options and dest.storage in {OnHeap, OnHeapNew, OnUnknown}: #writeStackTrace() #echo p.currLineInfo, " requesting" linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n", @@ -662,7 +662,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = # message(e.info, warnUser, "CAME HERE " & renderTree(e)) expr(p, e.sons[0], d) if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef: - d.s = OnHeap + d.storage = OnHeap else: var a: TLoc var typ = skipTypes(e.sons[0].typ, abstractInst) @@ -675,25 +675,25 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = initLocExprSingleUse(p, e.sons[0], a) if d.k == locNone: # dest = *a; <-- We do not know that 'dest' is on the heap! - # It is completely wrong to set 'd.s' here, unless it's not yet + # It is completely wrong to set 'd.storage' here, unless it's not yet # been assigned to. case typ.kind of tyRef: - d.s = OnHeap + d.storage = OnHeap of tyVar: - d.s = OnUnknown + d.storage = OnUnknown if tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a), a.s) + putIntoDest(p, d, e.typ, rdLoc(a), a.storage) return of tyPtr: - d.s = OnUnknown # BUGFIX! + d.storage = OnUnknown # BUGFIX! else: internalError(e.info, "genDeref " & $typ.kind) elif p.module.compileToCpp: if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a), a.s) + putIntoDest(p, d, e.typ, rdLoc(a), a.storage) return if enforceDeref and mt == ctPtrToArray: # we lie about the type for better C interop: 'ptr array[3,T]' is @@ -701,26 +701,26 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = # See tmissingderef. So we get rid of the deref instead. The codegen # ends up using 'memcpy' for the array assignment, # so the '&' and '*' cancel out: - putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.s) + putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.storage) else: - putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.s) + putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.storage) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, "&" & a.r, a.s) + putIntoDest(p, d, e.typ, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) else: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, addrLoc(a), a.s) + putIntoDest(p, d, e.typ, addrLoc(a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = - if d.k == locNone: d.s = a.s + if d.k == locNone: d.storage = if a.storage == OnHeapNew: OnHeap else: a.storage proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = initLocExpr(p, e.sons[0], a) @@ -742,7 +742,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) else: internalError(e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) - putIntoDest(p, d, tupType.sons[i], r, a.s) + putIntoDest(p, d, tupType.sons[i], r, a.storage) proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; resTyp: ptr PType = nil): PSym = @@ -769,15 +769,14 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = # we found a unique tuple type which lacks field information # so we use Field$i addf(r, ".Field$1", [rope(f.position)]) - putIntoDest(p, d, f.typ, r, a.s) + putIntoDest(p, d, f.typ, r, a.storage) else: var rtyp: PType let field = lookupFieldAgain(p, ty, f, r, addr rtyp) if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp) if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) addf(r, ".$1", [field.loc.r]) - putIntoDest(p, d, field.typ, r, a.s) - #d.s = a.s + putIntoDest(p, d, field.typ, r, a.storage) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) @@ -827,7 +826,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = internalError(e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field, ty) add(r, rfmt(nil, ".$1", field.loc.r)) - putIntoDest(p, d, field.typ, r, a.s) + putIntoDest(p, d, field.typ, r, a.storage) else: genRecordField(p, e.sons[0], d) @@ -854,16 +853,16 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = localError(x.info, errIndexOutOfBounds) d.inheritLocation(a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), - rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.s) + rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage) proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange) - if d.k == locNone: d.s = a.s + inheritLocation(d, a) putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) + rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -872,9 +871,9 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! - if d.k == locNone: d.s = a.s + inheritLocation(d, a) putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), - rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.s) + rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -892,11 +891,11 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", rdLoc(b), rdLoc(a), lenField(p)) - if d.k == locNone: d.s = OnHeap + if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = rfmt(nil, "(*$1)", a.r) putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), - rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.s) + rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage) proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses) @@ -1088,7 +1087,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = var sizeExpr = sizeExpr let typ = a.t var b: TLoc - initLoc(b, locExpr, a.t, OnHeap) + initLoc(b, locExpr, a.t, OnHeapNew) let refType = typ.skipTypes(abstractInst) assert refType.kind == tyRef let bt = refType.lastSon @@ -1098,7 +1097,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = let args = [getTypeDesc(p.module, typ), genTypeInfo(p.module, typ), sizeExpr] - if a.s == OnHeap and usesNativeGC(): + if a.storage in {OnHeap, OnHeapNew} and usesNativeGC(): # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) @@ -1128,8 +1127,8 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = let args = [getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), length] var call: TLoc - initLoc(call, locExpr, dest.t, OnHeap) - if dest.s == OnHeap and usesNativeGC(): + initLoc(call, locExpr, dest.t, OnHeapNew) + if dest.storage in {OnHeap, OnHeapNew} and usesNativeGC(): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: @@ -1155,6 +1154,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), a.rdLoc])) + d.storage = OnHeapNew gcUsage(e) proc genConstExpr(p: BProc, n: PNode): Rope @@ -1206,7 +1206,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = add(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t - tmp2.s = if isRef: OnHeap else: OnStack + tmp2.storage = if isRef: OnHeap else: OnStack expr(p, it.sons[1], tmp2) if d.k == locNone: @@ -1223,7 +1223,7 @@ proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) = for i in countup(0, sonsLen(t) - 1): initLoc(arr, locExpr, elemType(skipTypes(t.typ, typedescInst)), OnHeap) arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) - arr.s = OnHeap # we know that sequences are on the heap + arr.storage = OnHeap # we know that sequences are on the heap expr(p, t.sons[i], arr) gcUsage(t) @@ -1243,8 +1243,8 @@ proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) = for i in countup(0, L - 1): initLoc(elem, locExpr, elemType(skipTypes(t.typ, abstractInst)), OnHeap) elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) - elem.s = OnHeap # we know that sequences are on the heap - initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.s) + elem.storage = OnHeap # we know that sequences are on the heap + initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.storage) arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) @@ -1256,7 +1256,7 @@ proc genNewFinalize(p: BProc, e: PNode) = refType = skipTypes(e.sons[1].typ, abstractVarRange) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], f) - initLoc(b, locExpr, a.t, OnHeap) + initLoc(b, locExpr, a.t, OnHeapNew) ti = genTypeInfo(p.module, refType) addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ @@ -1308,7 +1308,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) else: r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) - putIntoDest(p, d, getSysType(tyBool), r, a.s) + putIntoDest(p, d, getSysType(tyBool), r, a.storage) proc genOf(p: BProc, n: PNode, d: var TLoc) = genOf(p, n.sons[1], n.sons[2].typ, d) @@ -1320,46 +1320,47 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: putIntoDest(p, d, e.typ, - ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.s) + ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage) of tyFloat..tyFloat128: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.s) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage) of tyBool: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.s) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage) of tyChar: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.s) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage) of tyEnum, tyOrdinal: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprEnum((NI)$1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t)]), a.s) + rdLoc(a), genTypeInfo(p.module, t)]), a.storage) of tyString: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.s) + putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage) of tySet: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [ - addrLoc(a), genTypeInfo(p.module, t)]), a.s) + addrLoc(a), genTypeInfo(p.module, t)]), a.storage) of tyOpenArray, tyVarargs: var b: TLoc case a.t.kind of tyOpenArray, tyVarargs: - putIntoDest(p, b, e.typ, "$1, $1Len_0" % [rdLoc(a)], a.s) + putIntoDest(p, b, e.typ, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: putIntoDest(p, b, e.typ, - "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.s) + "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage) of tyArray: putIntoDest(p, b, e.typ, - "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.s) + "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) else: internalError(e.sons[0].info, "genRepr()") putIntoDest(p, d, e.typ, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), - genTypeInfo(p.module, elemType(t))]), a.s) + genTypeInfo(p.module, elemType(t))]), a.storage) of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t)]), a.s) + rdLoc(a), genTypeInfo(p.module, t)]), a.storage) of tyEmpty, tyVoid: localError(e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", - [addrLoc(a), genTypeInfo(p.module, t)]), a.s) + [addrLoc(a), genTypeInfo(p.module, t)]), + a.storage) gcUsage(e) proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = @@ -1600,13 +1601,13 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = let etyp = skipTypes(e.typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: putIntoDest(p, d, e.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, e.typ), addrLoc(a)], a.s) + [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage) elif etyp.kind == tyProc and etyp.callConv == ccClosure: putIntoDest(p, d, e.typ, "(($1) ($2))" % - [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.s) + [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) else: putIntoDest(p, d, e.typ, "(($1) ($2))" % - [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.s) + [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) proc genCast(p: BProc, e: PNode, d: var TLoc) = const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray} @@ -1623,10 +1624,10 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl) tmp.k = locExpr tmp.t = srct - tmp.s = OnStack + tmp.storage = OnStack tmp.flags = {} expr(p, e.sons[1], tmp) - putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.s) + putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.storage) else: # I prefer the shorter cast version for pointer types -> generate less # C code; plus it's the right thing to do for closures: @@ -1640,13 +1641,13 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = {tyUInt..tyUInt64}: initLocExpr(p, n.sons[0], a) putIntoDest(p, d, n.typ, "(($1) ($2))" % - [getTypeDesc(p.module, dest), rdCharLoc(a)], a.s) + [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) else: initLocExpr(p, n.sons[0], a) putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ getTypeDesc(p.module, dest), rdCharLoc(a), genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), - rope(magic)]), a.s) + rope(magic)]), a.storage) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyGenericInst, tyAlias}) @@ -1658,13 +1659,15 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], a.s) + putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], + a.storage) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) putIntoDest(p, d, skipTypes(n.typ, abstractVar), - ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.s) + ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), + a.storage) gcUsage(n) proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = @@ -1872,7 +1875,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] - initLoc(rec, locExpr, it.typ, d.s) + initLoc(rec, locExpr, it.typ, d.storage) rec.r = "$1.Field$2" % [rdLoc(d), rope(i)] expr(p, it, rec) @@ -1911,7 +1914,7 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = if not handleConstExpr(p, n, d): if d.k == locNone: getTemp(p, n.typ, d) for i in countup(0, sonsLen(n) - 1): - initLoc(arr, locExpr, elemType(skipTypes(n.typ, abstractInst)), d.s) + initLoc(arr, locExpr, elemType(skipTypes(n.typ, abstractInst)), d.storage) arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)] expr(p, n.sons[i], arr) @@ -1950,10 +1953,10 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = r, genTypeInfo(p.module, dest)) if n.sons[0].typ.kind != tyObject: putIntoDest(p, d, n.typ, - "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.s) + "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) else: putIntoDest(p, d, n.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, dest), addrLoc(a)], a.s) + [getTypeDesc(p.module, dest), addrLoc(a)], a.storage) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: @@ -1985,9 +1988,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: r = "&" & r - putIntoDest(p, d, n.typ, r, a.s) + putIntoDest(p, d, n.typ, r, a.storage) else: - putIntoDest(p, d, n.typ, r, a.s) + putIntoDest(p, d, n.typ, r, a.storage) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = let t = n.typ @@ -2008,7 +2011,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = # This fixes bug #4551, but we really need better dataflow # analysis to make this 100% safe. if t.kind notin {tySequence, tyString}: - d.s = OnStatic + d.storage = OnStatic proc expr(p: BProc, n: PNode, d: var TLoc) = p.currLineInfo = n.info diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 8796dd729e..36b225ee58 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -57,7 +57,7 @@ proc genVarTuple(p: BProc, n: PNode) = else: assignLocalVar(p, v) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) - initLoc(field, locExpr, t.sons[i], tup.s) + initLoc(field, locExpr, t.sons[i], tup.storage) if t.kind == tyTuple: field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] else: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 35d73aac06..770c4e4689 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -287,7 +287,7 @@ proc fillResult(param: PSym) = OnStack) if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): incl(param.loc.flags, lfIndirect) - param.loc.s = OnUnknown + param.loc.storage = OnUnknown proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: @@ -404,7 +404,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, add(params, getTypeDescWeak(m, param.typ, check)) add(params, ~"*") incl(param.loc.flags, lfIndirect) - param.loc.s = OnUnknown + param.loc.storage = OnUnknown elif weakDep: add(params, getTypeDescWeak(m, param.typ, check)) else: @@ -417,7 +417,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, var j = 0 while arr.kind in {tyOpenArray, tyVarargs}: # this fixes the 'sort' bug: - if param.typ.kind == tyVar: param.loc.s = OnUnknown + if param.typ.kind == tyVar: param.loc.storage = OnUnknown # need to pass hidden parameter: addf(params, ", NI $1Len_$2", [param.loc.r, j.rope]) inc(j) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b618837c79..6c35a7e180 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -49,7 +49,7 @@ proc findPendingModule(m: BModule, s: PSym): BModule = proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = result.k = k - result.s = s + result.storage = s result.t = typ result.r = nil result.flags = {} @@ -59,7 +59,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: Rope, s: TStorageLoc) = if a.k == locNone: a.k = k a.t = typ - a.s = s + a.storage = s if a.r == nil: a.r = r proc isSimpleConst(typ: PType): bool = @@ -294,7 +294,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = else: if optNilCheck in p.options: linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc)) - if loc.s != OnStack: + if loc.storage != OnStack: linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", addrLoc(loc), genTypeInfo(p.module, loc.t)) # XXX: generated reset procs should not touch the m_type @@ -341,7 +341,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) result.k = locTemp result.t = t - result.s = OnStack + result.storage = OnStack result.flags = {} constructLoc(p, result, not needsInit) @@ -350,7 +350,7 @@ proc getIntTemp(p: BProc, result: var TLoc) = result.r = "T" & rope(p.labels) & "_" linefmt(p, cpsLocals, "NI $1;$n", result.r) result.k = locTemp - result.s = OnStack + result.storage = OnStack result.t = getSysType(tyInt) result.flags = {} @@ -695,7 +695,7 @@ proc genProcAux(m: BModule, prc: PSym) = assignParam(p, res) if skipTypes(res.typ, abstractInst).kind == tyArray: #incl(res.loc.flags, lfIndirect) - res.loc.s = OnUnknown + res.loc.storage = OnUnknown for i in countup(1, sonsLen(prc.typ.n) - 1): var param = prc.typ.n.sons[i].sym diff --git a/compiler/rodread.nim b/compiler/rodread.nim index f7e5a0f847..c06cf3414c 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -257,9 +257,9 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = loc.k = low(loc.k) if r.s[r.pos] == '*': inc(r.pos) - loc.s = TStorageLoc(decodeVInt(r.s, r.pos)) + loc.storage = TStorageLoc(decodeVInt(r.s, r.pos)) else: - loc.s = low(loc.s) + loc.storage = low(loc.storage) if r.s[r.pos] == '$': inc(r.pos) loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos))) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index d61d817dd2..90cafaf1b0 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -175,9 +175,9 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = var oldLen = result.len result.add('<') if loc.k != low(loc.k): encodeVInt(ord(loc.k), result) - if loc.s != low(loc.s): + if loc.storage != low(loc.storage): add(result, '*') - encodeVInt(ord(loc.s), result) + encodeVInt(ord(loc.storage), result) if loc.flags != {}: add(result, '$') encodeVInt(cast[int32](loc.flags), result) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 6e7d7993f7..8607b4bfc2 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -28,7 +28,7 @@ template createCb(retFutureSym, iteratorNameSym, name, futureVarCompletions: untyped) = var nameIterVar = iteratorNameSym #{.push stackTrace: off.} - proc cb0 {.closure.} = + proc cb0 {.closure, gcsafe.} = try: if not nameIterVar.finished: var next = nameIterVar() From 405b9c8a8535563f337e23dfbab8453738816b96 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 Sep 2017 14:38:45 +0200 Subject: [PATCH 15/56] refactoring: TLoc knows the node it originated from; it is planned to use this to produce better C code (move optimizations) --- compiler/ast.nim | 5 +- compiler/ccgcalls.nim | 6 +- compiler/ccgexprs.nim | 254 +++++++++++++++++++++--------------------- compiler/ccgstmts.nim | 24 ++-- compiler/ccgtypes.nim | 19 ++-- compiler/cgen.nim | 91 ++++++++------- compiler/rodread.nim | 5 +- compiler/rodwrite.nim | 7 +- 8 files changed, 215 insertions(+), 196 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 2b1871bb8c..5a99e0c823 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -755,7 +755,7 @@ type k*: TLocKind # kind of location storage*: TStorageLoc flags*: TLocFlags # location's flags - t*: PType # type of location + lode*: PNode # Node where the location came from; can be faked r*: Rope # rope value of location (code generators) # ---------------- end of backend information ------------------------------ @@ -1264,9 +1264,8 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(a.k): a.k = b.k if a.storage == low(a.storage): a.storage = b.storage a.flags = a.flags + b.flags - if a.t == nil: a.t = b.t + if a.lode == nil: a.lode = b.lode if a.r == nil: a.r = b.r - #if a.a == 0: a.a = b.a proc newSons*(father: PNode, length: int) = if isNil(father.sons): diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 7493a50ca8..e2e52839ab 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -56,7 +56,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc - initLoc(list, locCall, d.t, OnUnknown) + initLoc(list, locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: @@ -241,7 +241,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc - initLoc(list, locCall, d.t, OnUnknown) + initLoc(list, locCall, d.lode, OnUnknown) list.r = callPattern % [op.r, pl, pl.addComma, rawProc] genAssignment(p, d, list, {}) # no need for deep copying else: @@ -437,7 +437,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc - initLoc(list, locCall, d.t, OnUnknown) + initLoc(list, locCall, d.lode, 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 1064bf998f..fbc9aa98bd 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -203,7 +203,7 @@ proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != nil result.k = locField result.storage = if a.storage == OnHeapNew: OnHeap else: a.storage - result.t = t + result.lode = lodeTyp t result.r = rdLoc(a) & "." & field proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = @@ -407,11 +407,11 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = else: d = s # ``d`` is free, so fill it with ``s`` -proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = +proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locData, t, OnStatic) + initLoc(a, locData, n, OnStatic) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -419,14 +419,14 @@ proc putDataIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope) = # we cannot call initLoc() here as that would overwrite # the flags field! d.k = locData - d.t = t + d.lode = n d.r = r -proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) = +proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locExpr, t, s) + initLoc(a, locExpr, n, s) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -434,7 +434,7 @@ proc putIntoDest(p: BProc, d: var TLoc, t: PType, r: Rope; s=OnUnknown) = # we cannot call initLoc() here as that would overwrite # the flags field! d.k = locExpr - d.t = t + d.lode = n d.r = r proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = @@ -456,7 +456,7 @@ proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)])) + putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)])) proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a, b: TLoc @@ -464,17 +464,17 @@ proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc])) + putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc])) proc unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, e.sons[1], a) - putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdLoc(a)])) + putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)])) proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, e.sons[1], a) - putIntoDest(p, d, e.typ, ropecg(p.module, frmt, [rdCharLoc(a)])) + putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)])) proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; frmt: string): Rope = @@ -514,11 +514,11 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = let t = e.typ.skipTypes(abstractRange) if optOverflowCheck notin p.options: let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)] - putIntoDest(p, d, e.typ, res) + putIntoDest(p, d, e, res) else: let res = binaryArithOverflowRaw(p, t, a, b, if t.kind == tyInt64: prc64[m] else: prc[m]) - putIntoDest(p, d, e.typ, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) + putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = const @@ -535,7 +535,7 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if optOverflowCheck in p.options: linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n", rdLoc(a), intLiteral(firstOrd(t))) - putIntoDest(p, d, e.typ, opr[m] % [rdLoc(a), rope(getSize(t) * 8)]) + putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)]) proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = const @@ -593,7 +593,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(a.t), getSize(b.t)) * 8 k = getSize(a.t) * 8 - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), getSimpleTypeDesc(p.module, e.typ), rope(k)]) @@ -604,10 +604,10 @@ proc genEqProc(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) if a.t.skipTypes(abstractInst).callConv == ccClosure: - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)]) else: - putIntoDest(p, d, e.typ, "($1 == $2)" % [rdLoc(a), rdLoc(b)]) + putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)]) proc genIsNil(p: BProc, e: PNode, d: var TLoc) = let t = skipTypes(e.sons[1].typ, abstractRange) @@ -644,7 +644,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = assert(e.sons[1].typ != nil) initLocExpr(p, e.sons[1], a) t = skipTypes(e.typ, abstractRange) - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8), getSimpleTypeDesc(p.module, e.typ)]) @@ -684,7 +684,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = d.storage = OnUnknown if tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a), a.storage) + putIntoDest(p, d, e, rdLoc(a), a.storage) return of tyPtr: d.storage = OnUnknown # BUGFIX! @@ -693,7 +693,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = elif p.module.compileToCpp: if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: - putIntoDest(p, d, e.typ, rdLoc(a), a.storage) + putIntoDest(p, d, e, rdLoc(a), a.storage) return if enforceDeref and mt == ctPtrToArray: # we lie about the type for better C interop: 'ptr array[3,T]' is @@ -701,23 +701,23 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = # See tmissingderef. So we get rid of the deref instead. The codegen # ends up using 'memcpy' for the array assignment, # so the '&' and '*' cancel out: - putIntoDest(p, d, a.t.sons[0], rdLoc(a), a.storage) + putIntoDest(p, d, lodeTyp(a.t.sons[0]), rdLoc(a), a.storage) else: - putIntoDest(p, d, e.typ, "(*$1)" % [rdLoc(a)], a.storage) + putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, "&" & a.r, a.storage) + putIntoDest(p, d, e, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ): expr(p, e.sons[0], d) else: var a: TLoc initLocExpr(p, e.sons[0], a) - putIntoDest(p, d, e.typ, addrLoc(a), a.storage) + putIntoDest(p, d, e, addrLoc(a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = if a.storage == OnHeapNew: OnHeap else: a.storage @@ -742,7 +742,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) else: internalError(e.info, "genTupleElem") addf(r, ".Field$1", [rope(i)]) - putIntoDest(p, d, tupType.sons[i], r, a.storage) + putIntoDest(p, d, e, r, a.storage) proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; resTyp: ptr PType = nil): PSym = @@ -769,14 +769,14 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = # we found a unique tuple type which lacks field information # so we use Field$i addf(r, ".Field$1", [rope(f.position)]) - putIntoDest(p, d, f.typ, r, a.storage) + putIntoDest(p, d, e, r, a.storage) else: var rtyp: PType let field = lookupFieldAgain(p, ty, f, r, addr rtyp) if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp) if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty)) addf(r, ".$1", [field.loc.r]) - putIntoDest(p, d, field.typ, r, a.storage) + putIntoDest(p, d, e, r, a.storage) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) @@ -791,11 +791,11 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym; if op.magic == mNot: it = it.sons[1] let disc = it.sons[2].skipConv assert(disc.kind == nkSym) - initLoc(test, locNone, it.typ, OnStack) + initLoc(test, locNone, it, OnStack) initLocExpr(p, it.sons[1], u) var o = obj let d = lookupFieldAgain(p, origTy, disc.sym, o) - initLoc(v, locExpr, d.typ, OnUnknown) + initLoc(v, locExpr, disc, OnUnknown) v.r = o v.r.add(".") v.r.add(d.loc.r) @@ -826,11 +826,11 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = internalError(e.info, "genCheckedRecordField") # generate the checks: genFieldCheck(p, e, r, field, ty) add(r, rfmt(nil, ".$1", field.loc.r)) - putIntoDest(p, d, field.typ, r, a.storage) + putIntoDest(p, d, e.sons[0], r, a.storage) else: genRecordField(p, e.sons[0], d) -proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = +proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) @@ -852,19 +852,19 @@ proc genArrayElem(p: BProc, x, y: PNode, d: var TLoc) = if idx < firstOrd(ty) or idx > lastOrd(ty): localError(x.info, errIndexOutOfBounds) d.inheritLocation(a) - putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), + putIntoDest(p, d, n, rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage) -proc genCStringElem(p: BProc, x, y: PNode, d: var TLoc) = +proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) var ty = skipTypes(a.t, abstractVarRange) inheritLocation(d, a) - putIntoDest(p, d, elemType(skipTypes(ty, abstractVar)), + putIntoDest(p, d, n, rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) -proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = +proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) # emit range check: @@ -872,10 +872,10 @@ proc genOpenArrayElem(p: BProc, x, y: PNode, d: var TLoc) = linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) - putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), + putIntoDest(p, d, n, rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) -proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = +proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) initLocExpr(p, y, b) @@ -894,17 +894,17 @@ proc genSeqElem(p: BProc, x, y: PNode, d: var TLoc) = if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = rfmt(nil, "(*$1)", a.r) - putIntoDest(p, d, elemType(skipTypes(a.t, abstractVar)), + putIntoDest(p, d, n, rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage) proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind - of tyArray: genArrayElem(p, n.sons[0], n.sons[1], d) - of tyOpenArray, tyVarargs: genOpenArrayElem(p, n.sons[0], n.sons[1], d) - of tySequence, tyString: genSeqElem(p, n.sons[0], n.sons[1], d) - of tyCString: genCStringElem(p, n.sons[0], n.sons[1], d) + of tyArray: genArrayElem(p, n, n.sons[0], n.sons[1], d) + of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n.sons[0], n.sons[1], d) + of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d) + of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d) of tyTuple: genTupleElem(p, n, d) else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') @@ -1070,7 +1070,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = getTypeDesc(p.module, bt)]) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) - initLoc(dest, locExpr, bt, OnHeap) + initLoc(dest, locExpr, e.sons[2], OnHeap) getIntTemp(p, tmpL) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) dest.r = rfmt(nil, "$1->data[$2]", rdLoc(a), tmpL.r) @@ -1087,7 +1087,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = var sizeExpr = sizeExpr let typ = a.t var b: TLoc - initLoc(b, locExpr, a.t, OnHeapNew) + initLoc(b, locExpr, a.lode, OnHeapNew) let refType = typ.skipTypes(abstractInst) assert refType.kind == tyRef let bt = refType.lastSon @@ -1127,7 +1127,7 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = let args = [getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), length] var call: TLoc - initLoc(call, locExpr, dest.t, OnHeapNew) + initLoc(call, locExpr, dest.lode, OnHeapNew) if dest.storage in {OnHeap, OnHeapNew} and usesNativeGC(): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) @@ -1150,7 +1150,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) var a: TLoc initLocExpr(p, e.sons[1], a) - putIntoDest(p, d, e.typ, ropecg(p.module, + putIntoDest(p, d, e, ropecg(p.module, "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), a.rdLoc])) @@ -1163,7 +1163,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - fillLoc(d, locData, t, p.module.tmpBase & rope(id), OnStatic) + fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic) if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) @@ -1205,7 +1205,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = add(tmp2.r, ".") add(tmp2.r, field.loc.r) tmp2.k = locTemp - tmp2.t = field.loc.t + tmp2.lode = it.sons[1] tmp2.storage = if isRef: OnHeap else: OnStack expr(p, it.sons[1], tmp2) @@ -1214,37 +1214,37 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = else: genAssignment(p, d, tmp, {}) -proc genSeqConstr(p: BProc, t: PNode, d: var TLoc) = +proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) = var arr: TLoc if d.k == locNone: - getTemp(p, t.typ, d) + getTemp(p, n.typ, d) # generate call to newSeq before adding the elements per hand: - genNewSeqAux(p, d, intLiteral(sonsLen(t))) - for i in countup(0, sonsLen(t) - 1): - initLoc(arr, locExpr, elemType(skipTypes(t.typ, typedescInst)), OnHeap) + genNewSeqAux(p, d, intLiteral(sonsLen(n))) + for i in countup(0, sonsLen(n) - 1): + initLoc(arr, locExpr, n[i], OnHeap) arr.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) arr.storage = OnHeap # we know that sequences are on the heap - expr(p, t.sons[i], arr) - gcUsage(t) + expr(p, n[i], arr) + gcUsage(n) -proc genArrToSeq(p: BProc, t: PNode, d: var TLoc) = +proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = var elem, a, arr: TLoc - if t.sons[1].kind == nkBracket: - t.sons[1].typ = t.typ - genSeqConstr(p, t.sons[1], d) + if n.sons[1].kind == nkBracket: + n.sons[1].typ = n.typ + genSeqConstr(p, n.sons[1], d) return if d.k == locNone: - getTemp(p, t.typ, d) + getTemp(p, n.typ, d) # generate call to newSeq before adding the elements per hand: - var L = int(lengthOrd(t.sons[1].typ)) + var L = int(lengthOrd(n.sons[1].typ)) genNewSeqAux(p, d, intLiteral(L)) - initLocExpr(p, t.sons[1], a) + initLocExpr(p, n.sons[1], a) for i in countup(0, L - 1): - initLoc(elem, locExpr, elemType(skipTypes(t.typ, abstractInst)), OnHeap) + initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap) elem.r = rfmt(nil, "$1->data[$2]", rdLoc(d), intLiteral(i)) elem.storage = OnHeap # we know that sequences are on the heap - initLoc(arr, locExpr, elemType(skipTypes(t.sons[1].typ, abstractInst)), a.storage) + initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.sons[1].typ, abstractInst)), a.storage) arr.r = rfmt(nil, "$1[$2]", rdLoc(a), intLiteral(i)) genAssignment(p, elem, arr, {afDestIsNil, needToCopy}) @@ -1256,7 +1256,7 @@ proc genNewFinalize(p: BProc, e: PNode) = refType = skipTypes(e.sons[1].typ, abstractVarRange) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], f) - initLoc(b, locExpr, a.t, OnHeapNew) + initLoc(b, locExpr, a.lode, OnHeapNew) ti = genTypeInfo(p.module, refType) addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ @@ -1308,7 +1308,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = r = rfmt(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r)) else: r = rfmt(p.module, "($1)", genOfHelper(p, dest, r)) - putIntoDest(p, d, getSysType(tyBool), r, a.storage) + putIntoDest(p, d, x, r, a.storage) proc genOf(p: BProc, n: PNode, d: var TLoc) = genOf(p, n.sons[1], n.sons[2].typ, d) @@ -1319,53 +1319,53 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = var t = skipTypes(e.sons[1].typ, abstractVarRange) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, ropecg(p.module, "#reprInt((NI64)$1)", [rdLoc(a)]), a.storage) of tyFloat..tyFloat128: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage) + putIntoDest(p, d, e, ropecg(p.module, "#reprFloat($1)", [rdLoc(a)]), a.storage) of tyBool: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage) + putIntoDest(p, d, e, ropecg(p.module, "#reprBool($1)", [rdLoc(a)]), a.storage) of tyChar: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage) + putIntoDest(p, d, e, ropecg(p.module, "#reprChar($1)", [rdLoc(a)]), a.storage) of tyEnum, tyOrdinal: - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, ropecg(p.module, "#reprEnum((NI)$1, $2)", [ rdLoc(a), genTypeInfo(p.module, t)]), a.storage) of tyString: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage) + putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage) of tySet: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprSet($1, $2)", [ + putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [ addrLoc(a), genTypeInfo(p.module, t)]), a.storage) of tyOpenArray, tyVarargs: var b: TLoc case a.t.kind of tyOpenArray, tyVarargs: - putIntoDest(p, b, e.typ, "$1, $1Len_0" % [rdLoc(a)], a.storage) + putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: - putIntoDest(p, b, e.typ, + putIntoDest(p, b, e, "$1->data, $1->$2" % [rdLoc(a), lenField(p)], a.storage) of tyArray: - putIntoDest(p, b, e.typ, + putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) else: internalError(e.sons[0].info, "genRepr()") - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), genTypeInfo(p.module, elemType(t))]), a.storage) of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", [ rdLoc(a), genTypeInfo(p.module, t)]), a.storage) of tyEmpty, tyVoid: localError(e.info, "'repr' doesn't support 'void' type") else: - putIntoDest(p, d, e.typ, ropecg(p.module, "#reprAny($1, $2)", - [addrLoc(a), genTypeInfo(p.module, t)]), - a.storage) + putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", + [addrLoc(a), genTypeInfo(p.module, t)]), + a.storage) gcUsage(e) proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = let t = e.sons[1].typ - putIntoDest(p, d, e.typ, genTypeInfo(p.module, t)) + putIntoDest(p, d, e, genTypeInfo(p.module, t)) proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc @@ -1410,11 +1410,11 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: frmt = "$1 = ($2 ? $2->len : 0);$n" lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) - putIntoDest(p, d, e.typ, tmp.r) + putIntoDest(p, d, e, tmp.r) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? - if op == mHigh: putIntoDest(p, d, e.typ, rope(lastOrd(typ))) - else: putIntoDest(p, d, e.typ, rope(lengthOrd(typ))) + if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(typ))) + else: putIntoDest(p, d, e, rope(lengthOrd(typ))) else: internalError(e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = @@ -1472,7 +1472,7 @@ proc fewCmps(s: PNode): bool = result = sonsLen(s) <= 8 # 8 seems to be a good value proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = - putIntoDest(p, d, e.typ, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)]) + putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(b, a.t)]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = case int(getSize(skipTypes(e.sons[1].typ, abstractVar))) @@ -1501,7 +1501,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = else: e.sons[2] initLocExpr(p, ea, a) - initLoc(b, locExpr, e.typ, OnUnknown) + initLoc(b, locExpr, e, OnUnknown) b.r = rope("(") var length = sonsLen(e.sons[1]) for i in countup(0, length - 1): @@ -1515,7 +1515,7 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) if i < length - 1: add(b.r, " || ") add(b.r, ")") - putIntoDest(p, d, e.typ, b.r) + putIntoDest(p, d, e, b.r) else: assert(e.sons[1].typ != nil) assert(e.sons[2].typ != nil) @@ -1600,13 +1600,13 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[1], a) let etyp = skipTypes(e.typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: - putIntoDest(p, d, e.typ, "(*($1*) ($2))" % + putIntoDest(p, d, e, "(*($1*) ($2))" % [getTypeDesc(p.module, e.typ), addrLoc(a)], a.storage) elif etyp.kind == tyProc and etyp.callConv == ccClosure: - putIntoDest(p, d, e.typ, "(($1) ($2))" % + putIntoDest(p, d, e, "(($1) ($2))" % [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) else: - putIntoDest(p, d, e.typ, "(($1) ($2))" % + putIntoDest(p, d, e, "(($1) ($2))" % [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) proc genCast(p: BProc, e: PNode, d: var TLoc) = @@ -1623,11 +1623,11 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl) tmp.k = locExpr - tmp.t = srct + tmp.lode = lodeTyp srct tmp.storage = OnStack tmp.flags = {} expr(p, e.sons[1], tmp) - putIntoDest(p, d, e.typ, "LOC$#.dest" % [lbl], tmp.storage) + putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage) else: # I prefer the shorter cast version for pointer types -> generate less # C code; plus it's the right thing to do for closures: @@ -1640,11 +1640,11 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}: initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, n.typ, "(($1) ($2))" % + putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) else: initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ + putIntoDest(p, d, lodeTyp dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ getTypeDesc(p.module, dest), rdCharLoc(a), genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), rope(magic)]), a.storage) @@ -1659,13 +1659,13 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, skipTypes(n.typ, abstractVar), "$1->data" % [rdLoc(a)], + putIntoDest(p, d, n, "$1->data" % [rdLoc(a)], a.storage) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) - putIntoDest(p, d, skipTypes(n.typ, abstractVar), + putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.storage) gcUsage(n) @@ -1678,11 +1678,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = binaryExpr(p, e, d, "($1 == $2)") elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""): initLocExpr(p, e.sons[2], x) - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): initLocExpr(p, e.sons[1], x) - putIntoDest(p, d, e.typ, + putIntoDest(p, d, e, rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1695,9 +1695,9 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - putIntoDest(p, d, e.typ, rfmt(nil, "(($4)($2) $1 ($4)($3))", - rope(opr[m]), rdLoc(a), rdLoc(b), - getSimpleTypeDesc(p.module, e[1].typ))) + putIntoDest(p, d, e, rfmt(nil, "(($4)($2) $1 ($4)($3))", + rope(opr[m]), rdLoc(a), rdLoc(b), + getSimpleTypeDesc(p.module, e[1].typ))) if optNaNCheck in p.options: linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d)) if optInfCheck in p.options: @@ -1739,7 +1739,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tyVar}) let res = binaryArithOverflowRaw(p, ranged, a, b, if underlying.kind == tyInt64: fun64[op] else: fun[op]) - putIntoDest(p, a, ranged, "($#)($#)" % [ + putIntoDest(p, a, e.sons[1], "($#)($#)" % [ getTypeDesc(p.module, ranged), res]) of mConStrStr: genStrConcat(p, e, d) @@ -1765,7 +1765,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e.sons[1].typ.skipTypes({tyTypeDesc}) - putIntoDest(p, d, e.typ, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) + putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: @@ -1786,7 +1786,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: frmt = "$1 = $2->len;$n" lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) - putIntoDest(p, d, e.typ, tmp.r) + putIntoDest(p, d, e, tmp.r) of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n") of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n") of mSetLengthStr: genSetLengthStr(p, e, d) @@ -1828,7 +1828,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = var a, b, idx: TLoc if nfAllConst in e.flags: - putIntoDest(p, d, e.typ, genSetNode(p, e)) + putIntoDest(p, d, e, genSetNode(p, e)) else: if d.k == locNone: getTemp(p, e.typ, d) if getSize(e.typ) > 8: @@ -1875,7 +1875,7 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] - initLoc(rec, locExpr, it.typ, d.storage) + initLoc(rec, locExpr, it, d.storage) rec.r = "$1.Field$2" % [rdLoc(d), rope(i)] expr(p, it, rec) @@ -1891,7 +1891,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = var tmp = "CNSTCLOSURE" & rope(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) - putIntoDest(p, d, n.typ, tmp, OnStatic) + putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc initLocExpr(p, n.sons[0], a) @@ -1914,7 +1914,7 @@ proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = if not handleConstExpr(p, n, d): if d.k == locNone: getTemp(p, n.typ, d) for i in countup(0, sonsLen(n) - 1): - initLoc(arr, locExpr, elemType(skipTypes(n.typ, abstractInst)), d.storage) + initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage) arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)] expr(p, n.sons[i], arr) @@ -1952,11 +1952,11 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n", r, genTypeInfo(p.module, dest)) if n.sons[0].typ.kind != tyObject: - putIntoDest(p, d, n.typ, + putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) else: - putIntoDest(p, d, n.typ, "(*($1*) ($2))" % - [getTypeDesc(p.module, dest), addrLoc(a)], a.storage) + putIntoDest(p, d, n, "(*($1*) ($2))" % + [getTypeDesc(p.module, dest), addrLoc(a)], a.storage) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: @@ -1988,9 +1988,9 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: r = "&" & r - putIntoDest(p, d, n.typ, r, a.storage) + putIntoDest(p, d, n, r, a.storage) else: - putIntoDest(p, d, n.typ, r, a.storage) + putIntoDest(p, d, n, r, a.storage) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = let t = n.typ @@ -2005,9 +2005,9 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) if d.k == locNone: - fillLoc(d, locData, t, tmp, OnStatic) + fillLoc(d, locData, n, tmp, OnStatic) else: - putDataIntoDest(p, d, t, tmp) + putDataIntoDest(p, d, n, tmp) # This fixes bug #4551, but we really need better dataflow # analysis to make this 100% safe. if t.kind notin {tySequence, tyString}: @@ -2022,7 +2022,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skMethod: if {sfDispatcher, sfForward} * sym.flags != {}: # we cannot produce code for the dispatcher yet: - fillProcLoc(p.module, sym) + fillProcLoc(p.module, n) genProcPrototype(p.module, sym) else: genProc(p.module, sym) @@ -2034,19 +2034,19 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = localError(n.info, "request to generate code for .compileTime proc: " & sym.name.s) genProc(p.module, sym) - if sym.loc.r == nil or sym.loc.t == nil: + if sym.loc.r == nil or sym.loc.lode == nil: internalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if isSimpleConst(sym.typ): - putIntoDest(p, d, n.typ, genLiteral(p, sym.ast, sym.typ), OnStatic) + putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) of skEnumField: - putIntoDest(p, d, n.typ, rope(sym.position)) + putIntoDest(p, d, n, rope(sym.position)) of skVar, skForVar, skResult, skLet: if {sfGlobal, sfThread} * sym.flags != {}: - genVarPrototype(p.module, sym) + genVarPrototype(p.module, n) if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) @@ -2054,7 +2054,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if sfThread in sym.flags: accessThreadLocalVar(p, sym) if emulatedThreadVars(): - putIntoDest(p, d, sym.loc.t, "NimTV_->" & sym.loc.r) + putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r) else: putLocIntoDest(p, d, sym.loc) else: @@ -2075,12 +2075,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") of nkNilLit: if not isEmptyType(n.typ): - putIntoDest(p, d, n.typ, genLiteral(p, n)) + putIntoDest(p, d, n, genLiteral(p, n)) of nkStrLit..nkTripleStrLit: - putDataIntoDest(p, d, n.typ, genLiteral(p, n)) + putDataIntoDest(p, d, n, genLiteral(p, n)) of nkIntLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkCharLit: - putIntoDest(p, d, n.typ, genLiteral(p, n)) + putIntoDest(p, d, n, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: genLineDir(p, n) @@ -2100,7 +2100,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genCall(p, n, d) of nkCurly: if isDeepConstExpr(n) and n.len != 0: - putIntoDest(p, d, n.typ, genSetNode(p, n)) + putIntoDest(p, d, n, genSetNode(p, n)) else: genSetConstr(p, n, d) of nkBracket: @@ -2141,7 +2141,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkLambdaKinds: var sym = n.sons[namePos].sym genProc(p.module, sym) - if sym.loc.r == nil or sym.loc.t == nil: + if sym.loc.r == nil or sym.loc.lode == nil: internalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 36b225ee58..74ffb46038 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -48,16 +48,17 @@ proc genVarTuple(p: BProc, n: PNode) = initLocExpr(p, n.sons[L-1], tup) var t = tup.t.skipTypes(abstractInst) for i in countup(0, L-3): - var v = n.sons[i].sym + let vn = n.sons[i] + let v = vn.sym if sfCompileTime in v.flags: continue if sfGlobal in v.flags: - assignGlobalVar(p, v) + assignGlobalVar(p, vn) genObjectInit(p, cpsInit, v.typ, v.loc, true) registerGcRoot(p, v) else: - assignLocalVar(p, v) + assignLocalVar(p, vn) initLocalVar(p, v, immediateAsgn=isAssignedImmediately(n[L-1])) - initLoc(field, locExpr, t.sons[i], tup.storage) + initLoc(field, locExpr, vn, tup.storage) if t.kind == tyTuple: field.r = "$1.Field$2" % [rdLoc(tup), rope(i)] else: @@ -171,7 +172,7 @@ proc genBreakState(p: BProc, n: PNode) = lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)]) # lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)]) -proc genVarPrototypeAux(m: BModule, sym: PSym) +proc genVarPrototypeAux(m: BModule, n: PNode) proc genGotoVar(p: BProc; value: PNode) = if value.kind notin {nkCharLit..nkUInt64Lit}: @@ -180,7 +181,8 @@ proc genGotoVar(p: BProc; value: PNode) = lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) proc genSingleVar(p: BProc, a: PNode) = - let v = a.sons[0].sym + let vn = a.sons[0] + let v = vn.sym if sfCompileTime in v.flags: return if sfGoto in v.flags: # translate 'var state {.goto.} = X' into 'goto LX': @@ -195,7 +197,7 @@ proc genSingleVar(p: BProc, a: PNode) = if sfPure in v.flags: # v.owner.kind != skModule: targetProc = p.module.preInitProc - assignGlobalVar(targetProc, v) + assignGlobalVar(targetProc, vn) # XXX: be careful here. # Global variables should not be zeromem-ed within loops # (see bug #20). @@ -206,7 +208,7 @@ proc genSingleVar(p: BProc, a: PNode) = # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) if sfExportc in v.flags and p.module.g.generatedHeader != nil: - genVarPrototypeAux(p.module.g.generatedHeader, v) + genVarPrototypeAux(p.module.g.generatedHeader, vn) registerGcRoot(p, v) else: let value = a.sons[2] @@ -217,7 +219,7 @@ proc genSingleVar(p: BProc, a: PNode) = # parameterless constructor followed by an assignment operator. So we # generate better code here: genLineDir(p, a) - let decl = localVarDecl(p, v) + let decl = localVarDecl(p, vn) var tmp: TLoc if value.kind in nkCallKinds and value[0].kind == nkSym and sfConstructor in value[0].sym.flags: @@ -233,7 +235,7 @@ proc genSingleVar(p: BProc, a: PNode) = initLocExprSingleUse(p, value, tmp) lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc]) return - assignLocalVar(p, v) + assignLocalVar(p, vn) initLocalVar(p, v, imm) if a.sons[2].kind != nkEmpty: @@ -513,7 +515,7 @@ proc genParForStmt(p: BProc, t: PNode) = preserveBreakIdx: let forLoopVar = t.sons[0].sym var rangeA, rangeB: TLoc - assignLocalVar(p, forLoopVar) + assignLocalVar(p, t.sons[0]) #initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack) #discard mangleName(forLoopVar) let call = t.sons[1] diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 770c4e4689..1ca001b428 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,12 +282,13 @@ proc ccgIntroducedPtr(s: PSym): bool = result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options) else: result = false -proc fillResult(param: PSym) = - fillLoc(param.loc, locParam, param.typ, ~"Result", +proc fillResult(param: PNode) = + fillLoc(param.sym.loc, locParam, param, ~"Result", OnStack) - if mapReturnType(param.typ) != ctArray and isInvalidReturnType(param.typ): - incl(param.loc.flags, lfIndirect) - param.loc.storage = OnUnknown + let t = param.sym.typ + if mapReturnType(t) != ctArray and isInvalidReturnType(t): + incl(param.sym.loc.flags, lfIndirect) + param.sym.loc.storage = OnUnknown proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: @@ -398,7 +399,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, var param = t.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if params != nil: add(params, ~", ") - fillLoc(param.loc, locParam, param.typ, mangleParamName(m, param), + fillLoc(param.loc, locParam, t.n.sons[i], mangleParamName(m, param), param.paramStorageLoc) if ccgIntroducedPtr(param): add(params, getTypeDescWeak(m, param.typ, check)) @@ -496,12 +497,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode, let sname = mangleRecFieldName(m, field, rectype) let ae = if accessExpr != nil: "$1.$2" % [accessExpr, sname] else: sname - fillLoc(field.loc, locField, field.typ, ae, OnUnknown) + fillLoc(field.loc, locField, n, ae, OnUnknown) # for importcpp'ed objects, we only need to set field.loc, but don't # have to recurse via 'getTypeDescAux'. And not doing so prevents problems # with heavily templatized C++ code: if not isImportedCppType(rectype): - let fieldType = field.loc.t.skipTypes(abstractInst) + let fieldType = field.loc.lode.typ.skipTypes(abstractInst) if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: addf(result, "$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check), sname]) @@ -861,7 +862,7 @@ proc genProcHeader(m: BModule, prc: PSym): Rope = elif prc.typ.callConv == ccInline: result.add "static " var check = initIntSet() - fillLoc(prc.loc, locProc, prc.typ, mangleName(m, prc), OnUnknown) + fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown) genProcParams(m, prc.typ, rettype, params, check) # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 6c35a7e180..d0add6bcc4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -47,21 +47,31 @@ proc findPendingModule(m: BModule, s: PSym): BModule = var ms = getModule(s) result = m.g.modules[ms.position] -proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = +proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) = result.k = k result.storage = s - result.t = typ + result.lode = lode result.r = nil result.flags = {} -proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: Rope, s: TStorageLoc) = +proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) = # fills the loc if it is not already initialized if a.k == locNone: a.k = k - a.t = typ + a.lode = lode a.storage = s if a.r == nil: a.r = r +proc t(a: TLoc): PType {.inline.} = + if a.lode.kind == nkSym: + result = a.lode.sym.typ + else: + result = a.lode.typ + +proc lodeTyp(t: PType): PNode = + result = newNode(nkEmpty) + result.typ = t + proc isSimpleConst(typ: PType): bool = let t = skipTypes(typ, abstractVar) result = t.kind notin @@ -286,7 +296,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = if not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.t, OnStack) + initLoc(nilLoc, locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc, {afSrcIsNil}) else: @@ -340,7 +350,7 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = result.r = "T" & rope(p.labels) & "_" linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r) result.k = locTemp - result.t = t + result.lode = lodeTyp t result.storage = OnStack result.flags = {} constructLoc(p, result, not needsInit) @@ -351,7 +361,7 @@ proc getIntTemp(p: BProc, result: var TLoc) = linefmt(p, cpsLocals, "NI $1;$n", result.r) result.k = locTemp result.storage = OnStack - result.t = getSysType(tyInt) + result.lode = lodeTyp getSysType(tyInt) result.flags = {} proc initGCFrame(p: BProc): Rope = @@ -375,9 +385,10 @@ proc localDebugInfo(p: BProc, s: PSym) = inc(p.maxFrameLen) inc p.blocks[p.blocks.len-1].frameLen -proc localVarDecl(p: BProc; s: PSym): Rope = +proc localVarDecl(p: BProc; n: PNode): Rope = + let s = n.sym if s.loc.k == locNone: - fillLoc(s.loc, locLocalVar, s.typ, mangleLocalName(p, s), OnStack) + fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack) if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) result = getTypeDesc(p.module, s.typ) if s.constraint.isNil: @@ -390,23 +401,24 @@ proc localVarDecl(p: BProc; s: PSym): Rope = else: result = s.cgDeclFrmt % [result, s.loc.r] -proc assignLocalVar(p: BProc, s: PSym) = +proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! let nl = if optLineDir in gOptions: "" else: tnl - let decl = localVarDecl(p, s) & ";" & nl + let decl = localVarDecl(p, n) & ";" & nl line(p, cpsLocals, decl) - localDebugInfo(p, s) + localDebugInfo(p, n.sym) include ccgthreadvars proc varInDynamicLib(m: BModule, sym: PSym) proc mangleDynLibProc(sym: PSym): Rope -proc assignGlobalVar(p: BProc, s: PSym) = +proc assignGlobalVar(p: BProc, n: PNode) = + let s = n.sym if s.loc.k == locNone: - fillLoc(s.loc, locGlobalVar, s.typ, mangleName(p.module, s), OnHeap) + fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap) if lfDynamicLib in s.loc.flags: var q = findPendingModule(p.module, s) @@ -446,9 +458,10 @@ proc assignParam(p: BProc, s: PSym) = scopeMangledParam(p, s) localDebugInfo(p, s) -proc fillProcLoc(m: BModule; sym: PSym) = +proc fillProcLoc(m: BModule; n: PNode) = + let sym = n.sym if sym.loc.k == locNone: - fillLoc(sym.loc, locProc, sym.typ, mangleName(m, sym), OnStack) + fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack) proc getLabel(p: BProc): TLabel = inc(p.labels) @@ -457,7 +470,7 @@ proc getLabel(p: BProc): TLabel = proc fixLabel(p: BProc, labl: TLabel) = lineF(p, cpsStmts, "$1: ;$n", [labl]) -proc genVarPrototype(m: BModule, sym: PSym) +proc genVarPrototype(m: BModule, n: PNode) proc requestConstImpl(p: BProc, sym: PSym) proc genStmts(p: BProc, t: PNode) proc expr(p: BProc, n: PNode, d: var TLoc) @@ -469,11 +482,11 @@ proc genLiteral(p: BProc, n: PNode): Rope proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope proc initLocExpr(p: BProc, e: PNode, result: var TLoc) = - initLoc(result, locNone, e.typ, OnUnknown) + initLoc(result, locNone, e, OnUnknown) expr(p, e, result) proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = - initLoc(result, locNone, e.typ, OnUnknown) + initLoc(result, locNone, e, OnUnknown) result.flags.incl lfSingleUse expr(p, e, result) @@ -591,7 +604,7 @@ proc cgsym(m: BModule, name: string): Rope = if sym != nil: case sym.kind of skProc, skMethod, skConverter, skIterator: genProc(m, sym) - of skVar, skResult, skLet: genVarPrototype(m, sym) + of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym) of skType: discard getTypeDesc(m, sym.typ) else: internalError("cgsym: " & name & ": " & $sym.kind) else: @@ -645,7 +658,7 @@ proc closureSetup(p: BProc, prc: PSym) = internalError(prc.info, "closure generation failed") var env = ls.sym #echo "created environment: ", env.id, " for ", prc.name.s - assignLocalVar(p, env) + assignLocalVar(p, ls) # generate cast assignment: linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", rdLoc(env.loc), getTypeDesc(p.module, env.typ)) @@ -676,29 +689,30 @@ proc genProcAux(m: BModule, prc: PSym) = if sfPure notin prc.flags and prc.typ.sons[0] != nil: if resultPos >= prc.ast.len: internalError(prc.info, "proc has no result symbol") - var res = prc.ast.sons[resultPos].sym # get result symbol + let resNode = prc.ast.sons[resultPos] + let res = resNode.sym # get result symbol if not isInvalidReturnType(prc.typ.sons[0]): if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil): - var decl = localVarDecl(p, res) + var decl = localVarDecl(p, resNode) var a: TLoc initLocExprSingleUse(p, val, a) linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a)) else: # declare the result symbol: - assignLocalVar(p, res) + assignLocalVar(p, resNode) assert(res.loc.r != nil) initLocalVar(p, res, immediateAsgn=false) returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc)) else: - fillResult(res) + fillResult(resNode) assignParam(p, res) if skipTypes(res.typ, abstractInst).kind == tyArray: #incl(res.loc.flags, lfIndirect) res.loc.storage = OnUnknown for i in countup(1, sonsLen(prc.typ.n) - 1): - var param = prc.typ.n.sons[i].sym + let param = prc.typ.n.sons[i].sym if param.typ.isCompileTimeOnly: continue assignParam(p, param) closureSetup(p, prc) @@ -764,13 +778,13 @@ proc genProcPrototype(m: BModule, sym: PSym) = proc genProcNoForward(m: BModule, prc: PSym) = if lfImportCompilerProc in prc.loc.flags: - fillProcLoc(m, prc) + fillProcLoc(m, prc.ast[namePos]) useHeader(m, prc) # dependency to a compilerproc: discard cgsym(m, prc.name.s) return if lfNoDecl in prc.loc.flags: - fillProcLoc(m, prc) + fillProcLoc(m, prc.ast[namePos]) useHeader(m, prc) genProcPrototype(m, prc) elif prc.typ.callConv == ccInline: @@ -779,7 +793,7 @@ proc genProcNoForward(m: BModule, prc: PSym) = # a check for ``m.declaredThings``. if not containsOrIncl(m.declaredThings, prc.id): #if prc.loc.k == locNone: - fillProcLoc(m, prc) + fillProcLoc(m, prc.ast[namePos]) #elif {sfExportc, sfImportc} * prc.flags == {}: # # reset name to restore consistency in case of hashing collisions: # echo "resetting ", prc.id, " by ", m.module.name.s @@ -790,7 +804,7 @@ proc genProcNoForward(m: BModule, prc: PSym) = genProcAux(m, prc) elif lfDynamicLib in prc.loc.flags: var q = findPendingModule(m, prc) - fillProcLoc(q, prc) + fillProcLoc(q, prc.ast[namePos]) useHeader(m, prc) genProcPrototype(m, prc) if q != nil and not containsOrIncl(q.declaredThings, prc.id): @@ -799,13 +813,13 @@ proc genProcNoForward(m: BModule, prc: PSym) = symInDynamicLibPartial(m, prc) elif sfImportc notin prc.flags: var q = findPendingModule(m, prc) - fillProcLoc(q, prc) + fillProcLoc(q, prc.ast[namePos]) useHeader(m, prc) genProcPrototype(m, prc) if q != nil and not containsOrIncl(q.declaredThings, prc.id): genProcAux(q, prc) else: - fillProcLoc(m, prc) + fillProcLoc(m, prc.ast[namePos]) useHeader(m, prc) if sfInfixCall notin prc.flags: genProcPrototype(m, prc) @@ -813,7 +827,7 @@ proc requestConstImpl(p: BProc, sym: PSym) = var m = p.module useHeader(m, sym) if sym.loc.k == locNone: - fillLoc(sym.loc, locData, sym.typ, mangleName(p.module, sym), OnStatic) + fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic) if lfNoDecl in sym.loc.flags: return # declare implementation: var q = findPendingModule(m, sym) @@ -836,7 +850,7 @@ proc genProc(m: BModule, prc: PSym) = if sfBorrow in prc.flags or not isActivated(prc): return if sfForward in prc.flags: addForwardedProc(m, prc) - fillProcLoc(m, prc) + fillProcLoc(m, prc.ast[namePos]) else: genProcNoForward(m, prc) if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and @@ -846,10 +860,11 @@ proc genProc(m: BModule, prc: PSym) = if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id): genProcAux(m.g.generatedHeader, prc) -proc genVarPrototypeAux(m: BModule, sym: PSym) = +proc genVarPrototypeAux(m: BModule, n: PNode) = #assert(sfGlobal in sym.flags) + let sym = n.sym useHeader(m, sym) - fillLoc(sym.loc, locGlobalVar, sym.typ, mangleName(m, sym), OnHeap) + fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap) if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id): return if sym.owner.id != m.module.id: @@ -865,8 +880,8 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) = if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile") addf(m.s[cfsVars], " $1;$n", [sym.loc.r]) -proc genVarPrototype(m: BModule, sym: PSym) = - genVarPrototypeAux(m, sym) +proc genVarPrototype(m: BModule, n: PNode) = + genVarPrototypeAux(m, n) proc addIntTypes(result: var Rope) {.inline.} = addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl & diff --git a/compiler/rodread.nim b/compiler/rodread.nim index c06cf3414c..3735075fd0 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -267,9 +267,10 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) = loc.flags = {} if r.s[r.pos] == '^': inc(r.pos) - loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info) + loc.lode = decodeNode(r, info) + # rrGetType(r, decodeVInt(r.s, r.pos), info) else: - loc.t = nil + loc.lode = nil if r.s[r.pos] == '!': inc(r.pos) loc.r = rope(decodeStr(r.s, r.pos)) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 90cafaf1b0..9a8fafd86e 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -181,10 +181,11 @@ proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) = if loc.flags != {}: add(result, '$') encodeVInt(cast[int32](loc.flags), result) - if loc.t != nil: + if loc.lode != nil: add(result, '^') - encodeVInt(cast[int32](loc.t.id), result) - pushType(w, loc.t) + encodeNode(w, unknownLineInfo(), loc.lode, result) + #encodeVInt(cast[int32](loc.t.id), result) + #pushType(w, loc.t) if loc.r != nil: add(result, '!') encodeStr($loc.r, result) From 7db5bad016fa5794b2ec4fe7c6ea4303ecd97c65 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 Sep 2017 21:05:58 +0200 Subject: [PATCH 16/56] revert accidentical asyncmacro change --- lib/pure/asyncmacro.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 8607b4bfc2..6e7d7993f7 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -28,7 +28,7 @@ template createCb(retFutureSym, iteratorNameSym, name, futureVarCompletions: untyped) = var nameIterVar = iteratorNameSym #{.push stackTrace: off.} - proc cb0 {.closure, gcsafe.} = + proc cb0 {.closure.} = try: if not nameIterVar.finished: var next = nameIterVar() From 1c0c9d8959909ecf1db0ea689335f3c7baf9a729 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 Sep 2017 21:07:00 +0200 Subject: [PATCH 17/56] make tests green again --- compiler/semstmts.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index dbdb543f5c..2581c20e51 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -493,6 +493,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) = addSon(obj.n, newSymNode(field)) n.sons[0] = makeDeref x n.sons[1] = newSymNode(field) + n.typ = field.typ else: localError(n.info, "implicit object field construction " & "requires a .partial object, but got " & typeToString(obj)) From 3936587c06c60e57481952452b8258e8c5d58684 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 Sep 2017 21:07:18 +0200 Subject: [PATCH 18/56] fixes #6403 --- compiler/ast.nim | 3 +-- compiler/ccgexprs.nim | 38 +++++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 5a99e0c823..e3caa5d86f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -747,9 +747,8 @@ type OnUnknown, # location is unknown (stack, heap or static) OnStatic, # in a static section OnStack, # location is on hardware stack - OnHeap, # location is on heap or global + OnHeap # location is on heap or global # (reference counting needed) - OnHeapNew # location is on the heap and was just new()'ed TLocFlags* = set[TLocFlag] TLoc* = object k*: TLocKind # kind of location diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index fbc9aa98bd..25681c330a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -157,10 +157,23 @@ proc getStorageLoc(n: PNode): TStorageLoc = result = getStorageLoc(n.sons[0]) else: result = OnUnknown +proc canMove(n: PNode): bool = + # for now we're conservative here: + if n.kind == nkBracket: + # This needs to be kept consistent with 'const' seq code + # generation! + if not isDeepConstExpr(n) or n.len == 0: + if skipTypes(n.typ, abstractVarRange).kind == tySequence: + return true + result = n.kind in nkCallKinds + #if result: + # echo n.info, " optimized ", n + # result = false + proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = if dest.storage == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) - elif dest.storage in {OnHeap, OnHeapNew}: + elif dest.storage == OnHeap: # location is on heap # now the writer barrier is inlined for performance: # @@ -202,7 +215,7 @@ proc asgnComplexity(n: PNode): int = proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != nil result.k = locField - result.storage = if a.storage == OnHeapNew: OnHeap else: a.storage + result.storage = a.storage result.lode = lodeTyp t result.r = rdLoc(a) & "." & field @@ -274,18 +287,18 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if (needToCopy notin flags and src.storage != OnStatic) or src.storage == OnHeapNew: + if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(dest), rdLoc(src), genTypeInfo(p.module, dest.t)) of tyString: - if (needToCopy notin flags and src.storage != OnStatic) or src.storage == OnHeapNew: + if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: if dest.storage == OnStack or not usesNativeGC(): linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc) - elif dest.storage in {OnHeap, OnHeapNew}: + elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: var tmp: TLoc getTemp(p, ty, tmp) @@ -357,7 +370,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) else: internalError("genAssignment: " & $ty.kind) - if optMemTracker in p.options and dest.storage in {OnHeap, OnHeapNew, OnUnknown}: + if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}: #writeStackTrace() #echo p.currLineInfo, " requesting" linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n", @@ -720,7 +733,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, addrLoc(a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = - if d.k == locNone: d.storage = if a.storage == OnHeapNew: OnHeap else: a.storage + if d.k == locNone: d.storage = a.storage proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = initLocExpr(p, e.sons[0], a) @@ -1087,7 +1100,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = var sizeExpr = sizeExpr let typ = a.t var b: TLoc - initLoc(b, locExpr, a.lode, OnHeapNew) + initLoc(b, locExpr, a.lode, OnHeap) let refType = typ.skipTypes(abstractInst) assert refType.kind == tyRef let bt = refType.lastSon @@ -1097,7 +1110,7 @@ proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = let args = [getTypeDesc(p.module, typ), genTypeInfo(p.module, typ), sizeExpr] - if a.storage in {OnHeap, OnHeapNew} and usesNativeGC(): + if a.storage == OnHeap and usesNativeGC(): # use newObjRC1 as an optimization if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) @@ -1127,8 +1140,8 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope) = let args = [getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), length] var call: TLoc - initLoc(call, locExpr, dest.lode, OnHeapNew) - if dest.storage in {OnHeap, OnHeapNew} and usesNativeGC(): + initLoc(call, locExpr, dest.lode, OnHeap) + if dest.storage == OnHeap and usesNativeGC(): if canFormAcycle(dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) else: @@ -1154,7 +1167,6 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), genTypeInfo(p.module, seqtype), a.rdLoc])) - d.storage = OnHeapNew gcUsage(e) proc genConstExpr(p: BProc, n: PNode): Rope @@ -1256,7 +1268,7 @@ proc genNewFinalize(p: BProc, e: PNode) = refType = skipTypes(e.sons[1].typ, abstractVarRange) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], f) - initLoc(b, locExpr, a.lode, OnHeapNew) + initLoc(b, locExpr, a.lode, OnHeap) ti = genTypeInfo(p.module, refType) addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ From c383e4d35e4993f4cf094f984ae6acbd3f892a8b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 Sep 2017 09:57:18 +0200 Subject: [PATCH 19/56] preparations for string optimizations --- lib/nimbase.h | 10 ++++++---- lib/system.nim | 10 +++++++--- lib/system/sysstr.nim | 27 +++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/nimbase.h b/lib/nimbase.h index 70391024f6..34a2e43e7b 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -373,11 +373,13 @@ static N_INLINE(NI32, float32ToInt32)(float x) { #define float64ToInt64(x) ((NI64) (x)) +#define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */ + #define STRING_LITERAL(name, str, length) \ - static const struct { \ - TGenericSeq Sup; \ - NIM_CHAR data[(length) + 1]; \ - } name = {{length, length}, str} + static const struct { \ + TGenericSeq Sup; \ + NIM_CHAR data[(length) + 1]; \ + } name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str} typedef struct TStringDesc* string; diff --git a/lib/system.nim b/lib/system.nim index a4460570af..cc5ef9810c 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -409,8 +409,7 @@ when not defined(JS): when not defined(JS) and not defined(nimscript): template space(s: PGenericSeq): int {.dirty.} = - s.reserved and not seqShallowFlag - + s.reserved and not (seqShallowFlag or strlitFlag) include "system/hti" type @@ -1329,6 +1328,9 @@ const ## "amd64", "mips", "mipsel", "arm", "arm64", "mips64", "mips64el". seqShallowFlag = low(int) + strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \ + # emit this flag + # for string literals, it allows for some optimizations. {.push profiler: off.} when defined(nimKnowsNimvm): @@ -3720,7 +3722,9 @@ proc shallow*(s: var string) {.noSideEffect, inline.} = ## purposes. when not defined(JS) and not defined(nimscript): var s = cast[PGenericSeq](s) - s.reserved = s.reserved or seqShallowFlag + # string literals cannot become 'shallow': + if (s.reserved and strlitFlag) == 0: + s.reserved = s.reserved or seqShallowFlag type NimNodeObj = object diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 9ba53a5b87..8270bc525b 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -95,6 +95,11 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = if str == nil: NimString(nil) else: toNimStr(str, str.len) +when defined(nimImmutableStrings): + template wasMoved(x: NimString): bool = (x.reserved and seqShallowFlag) != 0 +else: + template wasMoved(x: NimString): bool = false + proc copyString(src: NimString): NimString {.compilerRtl.} = if src != nil: if (src.reserved and seqShallowFlag) != 0: @@ -103,6 +108,16 @@ proc copyString(src: NimString): NimString {.compilerRtl.} = result = rawNewStringNoInit(src.len) result.len = src.len copyMem(addr(result.data), addr(src.data), src.len + 1) + sysAssert((seqShallowFlag and result.reserved) == 0, "copyString") + when defined(nimImmutableStrings): + if (src.reserved and strlitFlag) != 0: + result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag + +proc newOwnedString(src: NimString; n: int): NimString = + result = rawNewStringNoInit(n) + result.len = n + copyMem(addr(result.data), addr(src.data), n) + result.data[n] = '\0' proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = if src != nil: @@ -116,6 +131,10 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = result = rawNewStringNoInit(src.len) result.len = src.len copyMem(addr(result.data), addr(src.data), src.len + 1) + sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1") + when defined(nimImmutableStrings): + if (src.reserved and strlitFlag) != 0: + result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag proc copyDeepString(src: NimString): NimString {.inline.} = if src != nil: @@ -144,6 +163,8 @@ proc addChar(s: NimString, c: char): NimString = result = cast[NimString](growObj(result, sizeof(TGenericSeq) + r + 1)) result.reserved = r + elif wasMoved(s): + result = newOwnedString(s, s.len) result.data[result.len] = c result.data[result.len+1] = '\0' inc(result.len) @@ -180,7 +201,7 @@ proc addChar(s: NimString, c: char): NimString = # s = rawNewString(0); proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = - if dest.len + addlen <= dest.space: + if dest.len + addlen <= dest.space and not wasMoved(dest): result = dest else: # slow path: var sp = max(resize(dest.space), dest.len + addlen) @@ -201,7 +222,9 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = var n = max(newLen, 0) - if n <= s.space: + if wasMoved(s): + result = newOwnedString(s, n) + elif n <= s.space: result = s else: result = resizeString(s, n) From fe99495a9bb4d1cc7127d98d5510b96a699225b1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 Sep 2017 10:15:30 +0200 Subject: [PATCH 20/56] new string behaviour now available under nimShallowStrings --- lib/system/sysstr.nim | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 8270bc525b..a8d91b39cf 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -95,10 +95,7 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = if str == nil: NimString(nil) else: toNimStr(str, str.len) -when defined(nimImmutableStrings): - template wasMoved(x: NimString): bool = (x.reserved and seqShallowFlag) != 0 -else: - template wasMoved(x: NimString): bool = false +template wasMoved(x: NimString): bool = (x.reserved and seqShallowFlag) != 0 proc copyString(src: NimString): NimString {.compilerRtl.} = if src != nil: @@ -109,7 +106,7 @@ proc copyString(src: NimString): NimString {.compilerRtl.} = result.len = src.len copyMem(addr(result.data), addr(src.data), src.len + 1) sysAssert((seqShallowFlag and result.reserved) == 0, "copyString") - when defined(nimImmutableStrings): + when defined(nimShallowStrings): if (src.reserved and strlitFlag) != 0: result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag @@ -132,7 +129,7 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = result.len = src.len copyMem(addr(result.data), addr(src.data), src.len + 1) sysAssert((seqShallowFlag and result.reserved) == 0, "copyStringRC1") - when defined(nimImmutableStrings): + when defined(nimShallowStrings): if (src.reserved and strlitFlag) != 0: result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag From 07531b41b9a3f1441c66ee157138915b34789ec1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 Sep 2017 11:12:14 +0200 Subject: [PATCH 21/56] make gc:v2 compile again --- lib/system/gc2.nim | 20 ++++++-------------- lib/system/gc_common.nim | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 083c06fe35..68da65727f 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -69,7 +69,7 @@ type maxStackCells: int # max stack cells in ``decStack`` cycleTableSize: int # max entries in cycle table maxPause: int64 # max measured GC pause in nanoseconds - + GcStack {.final, pure.} = object when nimCoroutines: prev: ptr GcStack @@ -220,17 +220,6 @@ else: x <% rcIncrement template `++`(x: expr): stmt = inc(x, rcIncrement) -proc prepareDealloc(cell: PCell) = - if cell.typ.finalizer != nil: - # the finalizer could invoke something that - # allocates memory; this could trigger a garbage - # collection. Since we are already collecting we - # prevend recursive entering here by a lock. - # XXX: we should set the cell's children to nil! - inc(gch.recGcLock) - (cast[Finalizer](cell.typ.finalizer))(cellToUsr(cell)) - dec(gch.recGcLock) - proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! discard @@ -249,6 +238,9 @@ proc incRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") c.refcount = c.refcount +% rcIncrement +proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = + decRef(usrToCell(p)) + proc nimGCref(p: pointer) {.compilerProc.} = let cell = usrToCell(p) incRef(cell) @@ -665,6 +657,8 @@ proc GC_dumpHeap() = # ---------------- cycle collector ------------------------------------------- +include gc_common + proc freeCyclicCell(gch: var GcHeap, c: PCell) = gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?") @@ -859,8 +853,6 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = add(gch.decStack, objStart) sysAssert(allocInv(gch.region), "gcMark end") -include gc_common - proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} = forEachStackSlot(gch, gcMark) diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 220331e964..484a4db9a0 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -373,12 +373,22 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) = ## is true. If ``allowGcAfterwards`` is true, a minimal amount of allocation ## happens to ensure the GC can continue to work after the call ## to ``deallocHeap``. + template deallocCell(x) = + if isCell(x): + # cast to PCell is correct here: + prepareDealloc(cast[PCell](x)) + if runFinalizers: - for x in allObjects(gch.region): - if isCell(x): - # cast to PCell is correct here: - var c = cast[PCell](x) - prepareDealloc(c) + when not declared(allObjectsAsProc): + for x in allObjects(gch.region): + deallocCell(x) + else: + var spaceIter: ObjectSpaceIter + while true: + let x = allObjectsAsProc(gch.region, addr spaceIter) + if spaceIter.state < 0: break + deallocCell(x) + deallocOsPages(gch.region) zeroMem(addr gch.region, sizeof(gch.region)) if allowGcAfterwards: From 5298a72f34ad2eb3774d6d70bd3176773cc0651f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 Sep 2017 13:29:45 +0200 Subject: [PATCH 22/56] NimScript: added buildOS and buildCPU constants --- compiler/ast.nim | 2 +- compiler/semfold.nim | 2 ++ lib/system/nimscript.nim | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 92abbb0ab0..e7af66c7aa 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -631,7 +631,7 @@ type mPointer, mEmptySet, mIntSetBaseType, mNil, mExpr, mStmt, mTypeDesc, mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy, mIsMainModule, mCompileDate, mCompileTime, mProcCall, - mCpuEndian, mHostOS, mHostCPU, mAppType, + mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType, mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 84cb0071f6..9733564e7a 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -616,6 +616,8 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n) of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n) + of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n) + of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n) of mAppType: result = getAppType(n) of mNaN: result = newFloatNodeT(NaN, n) of mInf: result = newFloatNodeT(Inf, n) diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 73bb91fef5..f5b9cf3ed5 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -11,6 +11,15 @@ # Nim's configuration system now uses Nim for scripting. This module provides # a few things that are required for this to work. +const + buildOS* {.magic: "BuildOS".}: string = "" + ## The OS this build is running on. Can be different from ``system.hostOS`` + ## for cross compilations. + + buildCPU* {.magic: "BuildCPU".}: string = "" + ## The CPU this build is running on. Can be different from ``system.hostCPU`` + ## for cross compilations. + template builtin = discard # We know the effects better than the compiler: From 7aad0d6544b14896cc83971aa0cdd6b5f96958ef Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 23 Sep 2017 16:29:05 +0200 Subject: [PATCH 23/56] added 'nim jsonscript' feature --- compiler/extccomp.nim | 97 +++++++++++++++++++++++++++++-------------- compiler/main.nim | 7 ++++ compiler/options.nim | 3 +- compiler/rodread.nim | 2 +- 4 files changed, 76 insertions(+), 33 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index c47e4fb9a4..49fdd9ea50 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -701,6 +701,40 @@ template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode) raise +proc execLinkCmd(linkCmd: string) = + tryExceptOSErrorMessage("invocation of external linker program failed."): + execExternalProgram(linkCmd, + if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking) + +proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) = + let runCb = proc (idx: int, p: Process) = + let exitCode = p.peekExitCode + if exitCode != 0: + rawMessage(errGenerated, "execution of an external compiler program '" & + cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & + p.outputStream.readAll.strip) + if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() + var res = 0 + if gNumberOfProcessors <= 1: + for i in countup(0, high(cmds)): + tryExceptOSErrorMessage("invocation of external compiler program failed."): + res = execWithEcho(cmds[i]) + if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) + else: + tryExceptOSErrorMessage("invocation of external compiler program failed."): + if optListCmd in gGlobalOptions or gVerbosity > 1: + res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) + elif gVerbosity == 1: + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, prettyCb, afterRunEvent=runCb) + else: + res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, + gNumberOfProcessors, afterRunEvent=runCb) + if res != 0: + if gNumberOfProcessors <= 1: + rawMessage(errExecutionOfProgramFailed, cmds.join()) + proc callCCompiler*(projectfile: string) = var linkCmd: string @@ -713,35 +747,9 @@ proc callCCompiler*(projectfile: string) = var prettyCmds: TStringSeq = @[] let prettyCb = proc (idx: int) = echo prettyCmds[idx] - let runCb = proc (idx: int, p: Process) = - let exitCode = p.peekExitCode - if exitCode != 0: - rawMessage(errGenerated, "execution of an external compiler program '" & - cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" & - p.outputStream.readAll.strip) compileCFile(toCompile, script, cmds, prettyCmds) if optCompileOnly notin gGlobalOptions: - if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors() - var res = 0 - if gNumberOfProcessors <= 1: - for i in countup(0, high(cmds)): - tryExceptOSErrorMessage("invocation of external compiler program failed."): - res = execWithEcho(cmds[i]) - if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i]) - else: - tryExceptOSErrorMessage("invocation of external compiler program failed."): - if optListCmd in gGlobalOptions or gVerbosity > 1: - res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath, poParentStreams}, - gNumberOfProcessors, afterRunEvent=runCb) - elif gVerbosity == 1: - res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, - gNumberOfProcessors, prettyCb, afterRunEvent=runCb) - else: - res = execProcesses(cmds, {poStdErrToStdOut, poUsePath, poParentStreams}, - gNumberOfProcessors, afterRunEvent=runCb) - if res != 0: - if gNumberOfProcessors <= 1: - rawMessage(errExecutionOfProgramFailed, cmds.join()) + execCmdsInParallel(cmds, prettyCb) if optNoLinking notin gGlobalOptions: # call the linker: var objfiles = "" @@ -756,9 +764,7 @@ proc callCCompiler*(projectfile: string) = linkCmd = getLinkCmd(projectfile, objfiles) if optCompileOnly notin gGlobalOptions: - tryExceptOSErrorMessage("invocation of external linker program failed."): - execExternalProgram(linkCmd, - if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking) + execLinkCmd(linkCmd) else: linkCmd = "" if optGenScript in gGlobalOptions: @@ -766,7 +772,8 @@ proc callCCompiler*(projectfile: string) = add(script, tnl) generateScript(projectfile, script) -from json import escapeJson +#from json import escapeJson +import json proc writeJsonBuildInstructions*(projectfile: string) = template lit(x: untyped) = f.write x @@ -834,6 +841,34 @@ proc writeJsonBuildInstructions*(projectfile: string) = lit "\L}\L" close(f) +proc runJsonBuildInstructions*(projectfile: string) = + let file = projectfile.splitFile.name + let jsonFile = toGeneratedFile(file, "json") + try: + let data = json.parseFile(jsonFile) + let toCompile = data["compile"] + doAssert toCompile.kind == JArray + var cmds: TStringSeq = @[] + var prettyCmds: TStringSeq = @[] + for c in toCompile: + doAssert c.kind == JArray + doAssert c.len >= 2 + + add(cmds, c[1].getStr) + let (_, name, _) = splitFile(c[0].getStr) + add(prettyCmds, "CC: " & name) + + let prettyCb = proc (idx: int) = + echo prettyCmds[idx] + execCmdsInParallel(cmds, prettyCb) + + let linkCmd = data["linkcmd"] + doAssert linkCmd.kind == JString + execLinkCmd(linkCmd.getStr) + except: + echo getCurrentException().getStackTrace() + quit "error evaluating JSON file: " & jsonFile + proc genMappingFiles(list: CFileList): Rope = for it in list: addf(result, "--file:r\"$1\"$N", [rope(it.cname)]) diff --git a/compiler/main.nim b/compiler/main.nim index f662ded1ba..450542c4c9 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -78,6 +78,10 @@ proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) = extccomp.callCCompiler(proj) extccomp.writeJsonBuildInstructions(proj) +proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) = + let proj = changeFileExt(gProjectFull, "") + extccomp.runJsonBuildInstructions(proj) + proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = #incl(gGlobalOptions, optSafeCode) setTarget(osJS, cpuJS) @@ -266,6 +270,9 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "nop", "help": # prevent the "success" message: gCmd = cmdDump + of "jsonscript": + gCmd = cmdJsonScript + commandJsonScript(graph, cache) else: rawMessage(errInvalidCommandX, command) diff --git a/compiler/options.nim b/compiler/options.nim index 9a5a1ae2a0..4888cc6ba7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -91,7 +91,8 @@ type cmdRst2html, # convert a reStructuredText file to HTML cmdRst2tex, # convert a reStructuredText file to TeX cmdInteractive, # start interactive session - cmdRun # run the project via TCC backend + cmdRun, # run the project via TCC backend + cmdJsonScript # compile a .json build file TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcNone, gcBoehm, gcGo, gcRegions, gcMarkAndSweep, gcRefc, diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 3735075fd0..31b54d760c 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -589,7 +589,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = return false of cmdNone, cmdDoc, cmdInterpret, cmdPretty, cmdGenDepend, cmdDump, cmdCheck, cmdParse, cmdScan, cmdIdeTools, cmdDef, - cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun: + cmdRst2html, cmdRst2tex, cmdInteractive, cmdRun, cmdJsonScript: discard # else: trigger recompilation: result = true From 1a61c4d46de11e957bdb6ff545bf52ccf15dbe06 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 23 Sep 2017 23:22:53 +0200 Subject: [PATCH 24/56] first implementation of the 'func' keyword --- compiler/aliases.nim | 2 +- compiler/ast.nim | 13 ++++++++----- compiler/ccgexprs.nim | 4 ++-- compiler/ccgstmts.nim | 2 +- compiler/cgen.nim | 2 +- compiler/docgen.nim | 14 ++++++++++---- compiler/jsgen.nim | 8 ++++---- compiler/lambdalifting.nim | 9 +++++---- compiler/lookups.nim | 6 ++++-- compiler/lowerings.nim | 2 +- compiler/parser.nim | 30 +++++++++++------------------- compiler/passes.nim | 2 +- compiler/renderer.nim | 5 ++++- compiler/rodwrite.nim | 2 +- compiler/sem.nim | 2 +- compiler/semcall.nim | 2 +- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 16 ++++++++-------- compiler/semfold.nim | 4 ++-- compiler/semgnrc.nim | 6 +++--- compiler/seminst.nim | 2 +- compiler/semparallel.nim | 2 +- compiler/sempass2.nim | 8 ++++---- compiler/semtempl.nim | 2 ++ compiler/semtypinst.nim | 2 +- compiler/sigmatch.nim | 8 ++++---- compiler/transf.nim | 2 +- compiler/types.nim | 8 ++++---- compiler/vmgen.nim | 4 ++-- compiler/writetracking.nim | 2 ++ lib/core/macros.nim | 8 +++++--- 31 files changed, 97 insertions(+), 84 deletions(-) diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 0c836bb24e..c0371e1596 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -95,7 +95,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = if a.kind == b.kind: case a.kind of nkSym: - const varKinds = {skVar, skTemp, skProc} + const varKinds = {skVar, skTemp, skProc, skFunc} # same symbol: aliasing: if a.sym.id == b.sym.id: result = arYes elif a.sym.kind in varKinds or b.sym.kind in varKinds: diff --git a/compiler/ast.nim b/compiler/ast.nim index 6e87959bfb..79e1105e13 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -221,6 +221,8 @@ type nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation + nkFuncDef # a func + TNodeKinds* = set[TNodeKind] type @@ -534,6 +536,7 @@ type skConst, # a constant skResult, # special 'result' variable skProc, # a proc + skFunc, # a func skMethod, # a method skIterator, # an iterator skConverter, # a type converter @@ -552,7 +555,7 @@ type TSymKinds* = set[TSymKind] const - routineKinds* = {skProc, skMethod, skIterator, + routineKinds* = {skProc, skFunc, skMethod, skIterator, skConverter, skMacro, skTemplate} tfIncompleteStruct* = tfVarargs tfUncheckedArray* = tfVarargs @@ -931,7 +934,7 @@ type # the poor naming choices in the standard library. const - OverloadableSyms* = {skProc, skMethod, skIterator, + OverloadableSyms* = {skProc, skFunc, skMethod, skIterator, skConverter, skModule, skTemplate, skMacro} GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, @@ -954,7 +957,7 @@ const tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, tyProc, tyString, tyError} - ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, + ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, skIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, @@ -978,14 +981,14 @@ const nkLiterals* = {nkCharLit..nkTripleStrLit} nkLambdaKinds* = {nkLambda, nkDo} - declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} + declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs* = nkLambdaKinds + declarativeDefs nkSymChoices* = {nkClosedSymChoice, nkOpenSymChoice} nkStrKinds* = {nkStrLit..nkTripleStrLit} skLocalVars* = {skVar, skLet, skForVar, skParam, skResult} - skProcKinds* = {skProc, skTemplate, skMacro, skIterator, + skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator, skMethod, skConverter} var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 25681c330a..88944aea6f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2039,7 +2039,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) - of skProc, skConverter, skIterator: + of skProc, skConverter, skIterator, skFunc: #if sym.kind == skIterator: # echo renderTree(sym.getBody, {renderIds}) if sfCompileTime in sym.flags: @@ -2205,7 +2205,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = discard of nkPragma: genPragma(p, n) of nkPragmaBlock: expr(p, n.lastSon, d) - of nkProcDef, nkMethodDef, nkConverterDef: + of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: if n.sons[genericParamsPos].kind == nkEmpty: var prc = n.sons[namePos].sym # due to a bug/limitation in the lambda lifting, unused inner procs diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 74ffb46038..eb32e7dd0f 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -960,7 +960,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = res.add(t.sons[i].strVal) of nkSym: var sym = t.sons[i].sym - if sym.kind in {skProc, skIterator, skMethod}: + if sym.kind in {skProc, skFunc, skIterator, skMethod}: var a: TLoc initLocExpr(p, t.sons[i], a) res.add($rdLoc(a)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d0add6bcc4..6970d09c3d 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -603,7 +603,7 @@ proc cgsym(m: BModule, name: string): Rope = var sym = magicsys.getCompilerProc(name) if sym != nil: case sym.kind - of skProc, skMethod, skConverter, skIterator: genProc(m, sym) + of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym) of skType: discard getTypeDesc(m, sym.typ) else: internalError("cgsym: " & name & ": " & $sym.kind) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 3f4f7b164a..8c50a4f1d4 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -325,7 +325,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string = ## section of ``doc/docgen.txt``. result = baseName case k: - of skProc: result.add(defaultParamSeparator) + of skProc, skFunc: result.add(defaultParamSeparator) of skMacro: result.add(".m" & defaultParamSeparator) of skMethod: result.add(".e" & defaultParamSeparator) of skIterator: result.add(".i" & defaultParamSeparator) @@ -341,7 +341,7 @@ proc isCallable(n: PNode): bool = ## Returns true if `n` contains a callable node. case n.kind of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, - nkConverterDef: result = true + nkConverterDef, nkFuncDef: result = true else: result = false @@ -533,6 +533,9 @@ proc generateDoc*(d: PDoc, n: PNode) = of nkProcDef: when useEffectSystem: documentRaises(n) genItem(d, n, n.sons[namePos], skProc) + of nkFuncDef: + when useEffectSystem: documentRaises(n) + genItem(d, n, n.sons[namePos], skFunc) of nkMethodDef: when useEffectSystem: documentRaises(n) genItem(d, n, n.sons[namePos], skMethod) @@ -574,6 +577,9 @@ proc generateJson*(d: PDoc, n: PNode) = of nkProcDef: when useEffectSystem: documentRaises(n) d.add genJsonItem(d, n, n.sons[namePos], skProc) + of nkFuncDef: + when useEffectSystem: documentRaises(n) + d.add genJsonItem(d, n, n.sons[namePos], skFunc) of nkMethodDef: when useEffectSystem: documentRaises(n) d.add genJsonItem(d, n, n.sons[namePos], skMethod) @@ -604,8 +610,8 @@ proc generateJson*(d: PDoc, n: PNode) = proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ - "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", - "Iterators", "Converters", "Macros", "Templates" + "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs", + "Methods", "Iterators", "Converters", "Macros", "Templates" ] if d.section[kind] == nil: return var title = sectionNames[kind].rope diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 73e6a99484..bffe196df4 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -292,7 +292,7 @@ proc useMagic(p: PProc, name: string) = if name.len == 0: return var s = magicsys.getCompilerProc(name) if s != nil: - internalAssert s.kind in {skProc, skMethod, skConverter} + internalAssert s.kind in {skProc, skFunc, skMethod, skConverter} if not p.g.generatedSyms.containsOrIncl(s.id): let code = genProc(p, s) add(p.g.constants, code) @@ -1111,7 +1111,7 @@ template isIndirect(x: PSym): bool = ({sfAddrTaken, sfGlobal} * v.flags != {} and #(mapType(v.typ) != etyObject) and {sfImportc, sfVolatile, sfExportc} * v.flags == {} and - v.kind notin {skProc, skConverter, skMethod, skIterator, + v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet} and p.target == targetJS) proc genAddr(p: PProc, n: PNode, r: var TCompRes) = @@ -1237,7 +1237,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: r.res = "$" & s.loc.r p.declareGlobal(s.id, r.res) - of skProc, skConverter, skMethod: + of skProc, skFunc, skConverter, skMethod: discard mangleName(s, p.target) if p.target == targetPHP and r.kind != resCallee: r.res = makeJsString($s.loc.r) @@ -2338,7 +2338,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef: discard of nkPragma: genPragma(p, n) - of nkProcDef, nkMethodDef, nkConverterDef: + of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: genSym(p, n.sons[namePos], r) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 986d8c716f..e64e0a898a 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -194,7 +194,7 @@ proc illegalCapture(s: PSym): bool {.inline.} = s.kind == skResult proc isInnerProc(s: PSym): bool = - if s.kind in {skProc, skMethod, skConverter, skIterator} and s.magic == mNone: + if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone: result = s.skipGenericOwner.kind in routineKinds proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = @@ -371,7 +371,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = case n.kind of nkSym: let s = n.sym - if s.kind in {skProc, skMethod, skConverter, skIterator} and s.typ != nil and s.typ.callConv == ccClosure: + if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and + s.typ != nil and s.typ.callConv == ccClosure: # this handles the case that the inner proc was declared as # .closure but does not actually capture anything: addClosureParam(c, s, n.info) @@ -443,7 +444,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = discard of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef: discard - of nkLambdaKinds, nkIteratorDef: + of nkLambdaKinds, nkIteratorDef, nkFuncDef: if n.typ != nil: detectCapturedVars(n[namePos], owner, c) else: @@ -730,7 +731,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; # now we know better, so patch it: n.sons[0] = x.sons[0] n.sons[1] = x.sons[1] - of nkLambdaKinds, nkIteratorDef: + of nkLambdaKinds, nkIteratorDef, nkFuncDef: if n.typ != nil and n[namePos].kind == nkSym: let m = newSymNode(n[namePos].sym) m.typ = n.typ diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 5a5dfc46a5..eddfeea567 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -144,8 +144,10 @@ type proc getSymRepr*(s: PSym): string = case s.kind - of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s) - else: result = s.name.s + of skProc, skFunc, skMethod, skConverter, skIterator: + result = getProcHeader(s) + else: + result = s.name.s proc ensureNoMissingOrUnusedSymbols(scope: PScope) = # check if all symbols have been used and defined: diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index ce76b63a41..033472c07f 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -633,7 +633,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType; if fn.kind == nkClosure: localError(n.info, "closure in spawn environment is not allowed") if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro, - skMethod, skConverter}): + skFunc, skMethod, skConverter}): # for indirect calls we pass the function pointer in the scratchObj var argType = n[0].typ.skipTypes(abstractInst) var field = newSym(skField, getIdent"fn", owner, n.info) diff --git a/compiler/parser.nim b/compiler/parser.nim index 253716247a..06ecf900e3 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -991,7 +991,7 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = if params.kind != nkEmpty: result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas) -proc parseProcExpr(p: var TParser, isExpr: bool): PNode = +proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? # either a proc type or a anonymous proc let info = parLineInfo(p) @@ -1002,7 +1002,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode = if p.tok.tokType == tkEquals and isExpr: getTok(p) skipComment(p, result) - result = newProcNode(nkLambda, info, parseStmt(p), + result = newProcNode(kind, info, parseStmt(p), params = params, pragmas = pragmas) else: @@ -1014,7 +1014,7 @@ proc parseProcExpr(p: var TParser, isExpr: bool): PNode = proc isExprStart(p: TParser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkIterator, tkBind, tkAddr, + tkProc, tkFunc, tkIterator, tkBind, tkAddr, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: result = true @@ -1088,21 +1088,12 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = case p.tok.tokType: of tkTuple: result = parseTuple(p, mode == pmTypeDef) - of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) + of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda) + of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef) of tkIterator: - when false: - if mode in {pmTypeDesc, pmTypeDef}: - result = parseProcExpr(p, false) - result.kind = nkIteratorTy - else: - # no anon iterators for now: - parMessage(p, errExprExpected, p.tok) - getTok(p) # we must consume a token here to prevend endless loops! - result = ast.emptyNode - else: - result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) - if result.kind == nkLambda: result.kind = nkIteratorDef - else: result.kind = nkIteratorTy + result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda) + if result.kind == nkLambda: result.kind = nkIteratorDef + else: result.kind = nkIteratorTy of tkEnum: if mode == pmTypeDef: result = parseEnum(p) @@ -2000,6 +1991,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkDefer: result = parseStaticOrDefer(p, nkDefer) of tkAsm: result = parseAsm(p) of tkProc: result = parseRoutine(p, nkProcDef) + of tkFunc: result = parseRoutine(p, nkFuncDef) of tkMethod: result = parseRoutine(p, nkMethodDef) of tkIterator: result = parseRoutine(p, nkIteratorDef) of tkMacro: result = parseRoutine(p, nkMacroDef) @@ -2056,8 +2048,8 @@ proc parseStmt(p: var TParser): PNode = else: # the case statement is only needed for better error messages: case p.tok.tokType - of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkIterator, - tkMacro, tkType, tkConst, tkWhen, tkVar: + of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc, + tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: parMessage(p, errComplexStmtRequiresInd) result = ast.emptyNode else: diff --git a/compiler/passes.nim b/compiler/passes.nim index bf6ce1a0a4..6efd50385c 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -64,7 +64,7 @@ proc astNeeded*(s: PSym): bool = # needs to be stored. The passes manager frees s.sons[codePos] when # appropriate to free the procedure body's memory. This is important # to keep memory usage down. - if (s.kind in {skMethod, skProc}) and + if (s.kind in {skMethod, skProc, skFunc}) and ({sfCompilerProc, sfCompileTime} * s.flags == {}) and (s.typ.callConv != ccInline) and (s.ast.sons[genericParamsPos].kind == nkEmpty): diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 220693f68b..c52161a557 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1166,6 +1166,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkProcDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkProc, "proc") gproc(g, n) + of nkFuncDef: + if renderNoProcDefs notin g.flags: putWithSpace(g, tkFunc, "func") + gproc(g, n) of nkConverterDef: if renderNoProcDefs notin g.flags: putWithSpace(g, tkConverter, "converter") gproc(g, n) @@ -1324,7 +1327,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if p.typ == nil or tfImplicitTypeParam notin p.typ.flags: return true return false - + if n.hasExplicitParams: put(g, tkBracketLe, "[") gsemicolon(g, n) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 9a8fafd86e..fb50c64739 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -580,7 +580,7 @@ proc process(c: PPassContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): discard process(c, n.sons[i]) #var s = n.sons[namePos].sym #addInterfaceSym(w, s) - of nkProcDef, nkIteratorDef, nkConverterDef, + of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef, nkTemplateDef, nkMacroDef: let s = n.sons[namePos].sym if s == nil: internalError(n.info, "rodwrite.process") diff --git a/compiler/sem.nim b/compiler/sem.nim index cd0df0de04..21fd0b3836 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -449,7 +449,7 @@ include semtypes, semtempl, semgnrc, semstmts, semexprs proc addCodeForGenerics(c: PContext, n: PNode) = for i in countup(c.lastGenericIdx, c.generics.len - 1): var prc = c.generics[i].inst.sym - if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone: + if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone: if prc.ast == nil or prc.ast.sons[bodyPos] == nil: internalError(prc.info, "no code for " & prc.name.s) else: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5984e25e08..9492e63f4e 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -468,7 +468,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = for i in countup(0, len(a)-1): var candidate = a.sons[i].sym if candidate.kind in {skProc, skMethod, skConverter, - skIterator}: + skFunc, skIterator}: # it suffices that the candidate has the proper number of generic # type parameters: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 97212d2cd7..a3f0f715bb 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -373,7 +373,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt; addSonSkipIntLit(result, intType) # basetype of range proc markIndirect*(c: PContext, s: PSym) {.inline.} = - if s.kind in {skProc, skConverter, skMethod, skIterator}: + if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}: incl(s.flags, sfAddrTaken) # XXX add to 'c' for global analysis diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9930e127e6..315efa4401 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -574,7 +574,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = optImplicitStatic notin gOptions: return if callee.magic notin ctfeWhitelist: return - if callee.kind notin {skProc, skConverter} or callee.isGenericRoutine: + if callee.kind notin {skProc, skFunc, skConverter} or callee.isGenericRoutine: return if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return @@ -616,10 +616,10 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, # for typeof support. # for ``type(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) else: result = semOverloadedCall(c, n, nOrig, - {skProc, skMethod, skConverter, skMacro, skTemplate}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags) if result != nil: if result.sons[0].kind != nkSym: @@ -1230,7 +1230,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = else: nil if s != nil: case s.kind - of skProc, skMethod, skConverter, skIterator: + of skProc, skFunc, skMethod, skConverter, skIterator: # type parameters: partial generic specialization n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) result = explicitGenericInstantiation(c, n, s) @@ -1398,7 +1398,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = proc semReturn(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) - if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or ( + if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or ( c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure): if n.sons[0].kind != nkEmpty: # transform ``return expr`` to ``result = expr; return`` @@ -2122,7 +2122,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) result = semSym(c, n, s, flags) - if s.kind in {skProc, skMethod, skConverter, skIterator}: + if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: @@ -2217,7 +2217,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = errorUseQualifier(c, n.info, s) elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) - of skProc, skMethod, skConverter, skIterator: + of skProc, skFunc, skMethod, skConverter, skIterator: if s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) else: @@ -2330,7 +2330,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkYieldStmt: result = semYield(c, n) of nkPragma: pragma(c, c.p.owner, n, stmtPragmas) of nkIteratorDef: result = semIterator(c, n) - of nkProcDef: result = semProc(c, n) + of nkProcDef, nkFuncDef: result = semProc(c, n) of nkMethodDef: result = semMethod(c, n) of nkConverterDef: result = semConverterDef(c, n) of nkMacroDef: result = semMacroDef(c, n) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 9733564e7a..d961a47a3d 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -630,7 +630,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = newStrNodeT(lookupSymbol(s.name), n) else: result = copyTree(s.ast) - of {skProc, skMethod}: + of {skProc, skFunc, skMethod}: result = n of skType: # XXX gensym'ed symbols can come here and cannot be resolved. This is @@ -654,7 +654,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of nkCallKinds: if n.sons[0].kind != nkSym: return var s = n.sons[0].sym - if s.kind != skProc: return + if s.kind != skProc and s.kind != skFunc: return try: case s.magic of mNone: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e3d078432d..3cdb68df64 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -61,7 +61,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterator, skConverter, skModule: + of skProc, skFunc, skMethod, skIterator, skConverter, skModule: result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpandSym(s): @@ -243,7 +243,7 @@ proc semGenericStmt(c: PContext, n: PNode, of skUnknown, skParam: # Leave it as an identifier. discard - of skProc, skMethod, skIterator, skConverter, skModule: + of skProc, skFunc, skMethod, skIterator, skConverter, skModule: result.sons[0] = sc # do not check of 's.magic==mRoof' here because it might be some # other '^' but after overload resolution the proper one: @@ -437,7 +437,7 @@ proc semGenericStmt(c: PContext, n: PNode, for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skParam) of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef, nkLambdaKinds: + nkFuncDef, nkIteratorDef, nkLambdaKinds: checkSonsLen(n, bodyPos + 1) if n.sons[namePos].kind != nkEmpty: addTempDecl(c, getIdentNode(n.sons[0]), skProc) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index a28d322b13..6abb34e90f 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -36,7 +36,7 @@ proc rawPushProcCon(c: PContext, owner: PSym) = c.p = x proc rawHandleSelf(c: PContext; owner: PSym) = - const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro} + const callableSymbols = {skProc, skFunc, skMethod, skConverter, skIterator, skMacro} if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil: let params = owner.typ.n if params.len > 1: diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 90c1a315a2..2581f5728a 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -387,7 +387,7 @@ proc analyse(c: var AnalysisCtx; n: PNode) = addFactNeg(c.guards, canon(n.sons[0])) dec c.inLoop of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, - nkMacroDef, nkTemplateDef, nkConstSection, nkPragma: + nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef: discard else: analyseSons(c, n) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e24c5fd295..e1a3939fc4 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -835,7 +835,7 @@ proc track(tracked: PEffects, n: PNode) = setLen(tracked.locked, oldLocked) tracked.currLockLevel = oldLockLevel of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, - nkMacroDef, nkTemplateDef, nkLambda, nkDo: + nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: discard of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: if n.len == 2: track(tracked, n.sons[1]) @@ -937,7 +937,7 @@ proc trackProc*(s: PSym, body: PNode) = track(t, body) if not isEmptyType(s.typ.sons[0]) and {tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and - s.kind in {skProc, skConverter, skMethod}: + s.kind in {skProc, skFunc, skConverter, skMethod}: var res = s.ast.sons[resultPos].sym # get result symbol if res.id notin t.init: message(body.info, warnProveInit, "result") @@ -979,10 +979,10 @@ proc trackProc*(s: PSym, body: PNode) = message(s.info, warnLockLevel, "declared lock level is $1, but real lock level is $2" % [$s.typ.lockLevel, $t.maxLockLevel]) - when useWriteTracking: trackWrites(s, body) + if s.kind == skFunc: trackWrites(s, body) proc trackTopLevelStmt*(module: PSym; n: PNode) = - if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, + if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}: return var effects = newNode(nkEffectList, n.info) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 8ad8a6288a..be8567c9cb 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -449,6 +449,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = a.sons[2] = semTemplBody(c, a.sons[2]) of nkProcDef, nkLambdaKinds: result = semRoutineInTemplBody(c, n, skProc) + of nkFuncDef: + result = semRoutineInTemplBody(c, n, skFunc) of nkMethodDef: result = semRoutineInTemplBody(c, n, skMethod) of nkIteratorDef: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index b4a61deb70..a3953d87e3 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -170,7 +170,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode = if isTypeParam(n[i]): needsFixing = true if needsFixing: n.sons[0] = newSymNode(n.sons[0].sym.owner) - return cl.c.semOverloadedCall(cl.c, n, n, {skProc}, {}) + return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {}) for i in 0 .. Date: Sat, 23 Sep 2017 23:29:23 +0200 Subject: [PATCH 25/56] write tracking for funcs implemented --- compiler/semexprs.nim | 3 ++- compiler/semstmts.nim | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 315efa4401..903db115f9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2330,7 +2330,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkYieldStmt: result = semYield(c, n) of nkPragma: pragma(c, c.p.owner, n, stmtPragmas) of nkIteratorDef: result = semIterator(c, n) - of nkProcDef, nkFuncDef: result = semProc(c, n) + of nkProcDef: result = semProc(c, n) + of nkFuncDef: result = semFunc(c, n) of nkMethodDef: result = semMethod(c, n) of nkConverterDef: result = semConverterDef(c, n) of nkMacroDef: result = semMacroDef(c, n) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2581c20e51..35f0cf6dc3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1604,6 +1604,9 @@ proc semIterator(c: PContext, n: PNode): PNode = proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) +proc semFunc(c: PContext, n: PNode): PNode = + result = semProcAux(c, n, skFunc, procPragmas) + proc semMethod(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") result = semProcAux(c, n, skMethod, methodPragmas) From e5cb1a2dd547827ee750abbb21039d1cf0a30988 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 24 Sep 2017 01:22:48 +0200 Subject: [PATCH 26/56] newSeqOfCap: skip initialization step for non-GC-ed data --- lib/system/mmdisp.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index d2160fdac4..8249349669 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -564,7 +564,11 @@ else: when not declared(nimNewSeqOfCap): proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} = - result = newObj(typ, addInt(mulInt(cap, typ.base.size), GenericSeqSize)) + let s = addInt(mulInt(cap, typ.base.size), GenericSeqSize) + when declared(newObjNoInit): + result = if ntfNoRefs in typ.base.flags: newObjNoInit(typ, s) else: newObj(typ, s) + else: + result = newObj(typ, s) cast[PGenericSeq](result).len = 0 cast[PGenericSeq](result).reserved = cap From e879da5791f66f8c7220885682ed623e7dd0daea Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 24 Sep 2017 11:21:52 +0200 Subject: [PATCH 27/56] some work to make 'opt' a first class type --- compiler/ast.nim | 12 ++----- compiler/canonicalizer.nim | 2 +- compiler/ccgutils.nim | 4 +-- compiler/jsgen.nim | 10 +++--- compiler/semasgn.nim | 4 +-- compiler/semexprs.nim | 10 ++---- compiler/semstmts.nim | 2 +- compiler/semtypes.nim | 2 ++ compiler/sighashes.nim | 2 +- compiler/sigmatch.nim | 2 +- compiler/types.nim | 68 ++++++++++++++++---------------------- compiler/vmdeps.nim | 3 +- lib/core/macros.nim | 2 +- 13 files changed, 52 insertions(+), 71 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 79e1105e13..8237c161eb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -404,14 +404,8 @@ type # instantiation and prior to this it has the potential to # be any type. - tyFieldAccessor - # Expressions such as Type.field (valid in contexts such - # as the `is` operator and magics like `high` and `low`). - # Could be lifted to a single argument proc returning the - # field value. - # sons[0]: type of containing object or tuple - # sons[1]: field type - # .n: nkDotExpr storing the field name + tyOpt + # Builtin optional type tyVoid # now different from tyEmpty, hurray! @@ -624,7 +618,7 @@ type mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, mReset, - mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, + mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mInt, mInt8, mInt16, mInt32, mInt64, diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index d17d928c85..6972f5acfa 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -130,7 +130,7 @@ proc hashType(c: var MD5Context, t: PType) = c.hashSym body.sym for i in countup(1, sonsLen(t) - 2): c.hashType t.sons[i] - of tyFromExpr, tyFieldAccessor: + of tyFromExpr: c.hashTree(t.n) of tyArray: c.hashTree(t.sons[0].n) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 6a7aa89515..315efbd641 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -100,7 +100,7 @@ proc getUniqueType*(key: PType): PType = if result == nil: gCanonicalTypes[k] = key result = key - of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor: + of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr: if key.isResolvedUserTypeClass: return getUniqueType(lastSon(key)) if key.sym != nil: @@ -126,7 +126,7 @@ proc getUniqueType*(key: PType): PType = result = slowSearch(key, k) of tyGenericInvocation, tyGenericBody, tyOpenArray, tyArray, tySet, tyRange, tyTuple, - tySequence, tyForward, tyVarargs, tyProxy: + tySequence, tyForward, tyVarargs, tyProxy, tyOpt: # we have to do a slow linear search because types may need # to be compared by their structure: result = slowSearch(key, k) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index bffe196df4..0f8d763c27 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -187,12 +187,12 @@ proc mapType(typ: PType): TJSTypeKind = of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat of tySet: result = etyObject # map a set to a table - of tyString, tySequence: result = etySeq + of tyString, tySequence, tyOpt: result = etySeq of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs: result = etyObject of tyNil: result = etyNull of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvocation, - tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor, + tyNone, tyFromExpr, tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClasses, tyVoid, tyAlias: result = etyNone of tyInferred: @@ -927,7 +927,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = # we don't care if it's an etyBaseIndex (global) of a string, it's # still a string that needs to be copied properly: - if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}: + if x.typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}: xtyp = etySeq case xtyp of etySeq: @@ -971,7 +971,7 @@ proc genFastAsgn(p: PProc, n: PNode) = # See bug #5933. So we try to be more compatible with the C backend semantics # here for 'shallowCopy'. This is an educated guess and might require further # changes later: - let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString} + let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString} genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy) proc genSwap(p: PProc, n: PNode) = @@ -1550,7 +1550,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("[null, 0]", indirect) else: result = putToSeq("null", indirect) - of tySequence, tyString, tyCString, tyPointer, tyProc: + of tySequence, tyOpt, tyString, tyCString, tyPointer, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index f2144037c6..b51d7167b2 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -185,7 +185,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, - tyPtr, tyString, tyRef: + tyPtr, tyString, tyRef, tyOpt: defaultOp(c, t, body, x, y) of tyArray, tySequence: if tfHasAsgn in t.flags: @@ -227,7 +227,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = tyTypeDesc, tyGenericInvocation, tyForward: internalError(c.info, "assignment requested for type: " & typeToString(t)) of tyOrdinal, tyRange, tyInferred, - tyGenericInst, tyFieldAccessor, tyStatic, tyVar, tyAlias: + tyGenericInst, tyStatic, tyVar, tyAlias: liftBodyAux(c, lastSon(t), body, x, y) of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("liftBodyAux") diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 903db115f9..fb86eae05c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -246,8 +246,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = localError(n.info, errXExpectsTypeOrValue, opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange + - {tyTypeDesc, tyFieldAccessor}) + var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc}) case typ.kind of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: n.typ = getSysType(tyInt) @@ -255,7 +254,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = n.typ = typ.sons[0] # indextype of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32: # do not skip the range! - n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor}) + n.typ = n.sons[1].typ.skipTypes(abstractVar) of tyGenericParam: # prepare this for resolving in semtypinst: # we must use copyTree here in order to avoid creating a cycle @@ -279,7 +278,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = n[1].typ != nil and n[1].typ.kind == tyTypeDesc and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor}) + let t1 = n[1].typ.skipTypes({tyTypeDesc}) if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize @@ -1087,9 +1086,6 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if field != nil: n.typ = makeTypeDesc(c, field.typ) return n - #n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.typ]) - #n.typ.n = copyTree(n) - #return n else: tryReadingGenericParam(ty) return diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 35f0cf6dc3..7ad13bebbf 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1234,7 +1234,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = s.typ = n.typ for i in 1.. 0: result = firstOrd(lastSon(t)) @@ -639,7 +619,7 @@ proc firstOrd(t: PType): BiggestInt = internalError("invalid kind for first(" & $t.kind & ')') result = 0 -proc lastOrd(t: PType): BiggestInt = +proc lastOrd*(t: PType): BiggestInt = case t.kind of tyBool: result = 1 of tyChar: result = 255 @@ -666,7 +646,7 @@ proc lastOrd(t: PType): BiggestInt = of tyEnum: assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) result = t.n.sons[sonsLen(t.n) - 1].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyFieldAccessor, tyAlias: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: result = lastOrd(lastSon(t)) of tyProxy: result = 0 of tyOrdinal: @@ -676,7 +656,7 @@ proc lastOrd(t: PType): BiggestInt = internalError("invalid kind for last(" & $t.kind & ')') result = 0 -proc lengthOrd(t: PType): BiggestInt = +proc lengthOrd*(t: PType): BiggestInt = case t.kind of tyInt64, tyInt32, tyInt: result = lastOrd(t) of tyDistinct: result = lengthOrd(t.sons[0]) @@ -689,6 +669,14 @@ proc lengthOrd(t: PType): BiggestInt = else: result = lastOrd(t) - firstOrd(t) + 1 +proc isCompatibleToCString*(a: PType): bool = + if a.kind == tyArray: + if (firstOrd(a.sons[0]) == 0) and + (skipTypes(a.sons[0], {tyRange, tyGenericInst, tyAlias}).kind in + {tyInt..tyInt64, tyUInt..tyUInt64}) and + (a.sons[1].kind == tyChar): + result = true + # -------------- type equality ----------------------------------------------- type @@ -989,7 +977,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = a.sym.position == b.sym.position of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, - tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyFieldAccessor: + tyArray, tyProc, tyVarargs, tyOrdinal, tyTypeClasses, tyOpt: cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n result = sameChildrenAux(a, b, c) and sameFlags(a, b) @@ -1144,7 +1132,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyTypeClasses: if not (tfGenericTypeParam in t.flags or taField notin flags): result = t of tyGenericBody, tyGenericParam, tyGenericInvocation, - tyNone, tyForward, tyFromExpr, tyFieldAccessor: + tyNone, tyForward, tyFromExpr: result = t of tyNil: if kind != skConst: result = t @@ -1160,7 +1148,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyOpenArray, tyVarargs: if kind != skParam: result = t else: result = typeAllowedAux(marker, t.sons[0], skVar, flags) - of tySequence: + of tySequence, tyOpt: if t.sons[0].kind != tyEmpty: result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) of tyArray: @@ -1359,7 +1347,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = typ.size = result typ.align = int16(a) -proc computeSize(typ: PType): BiggestInt = +proc computeSize*(typ: PType): BiggestInt = var a: BiggestInt = 1 result = computeSizeAux(typ, a) @@ -1368,7 +1356,7 @@ proc getReturnType*(s: PSym): PType = assert s.kind in skProcKinds result = s.typ.sons[0] -proc getSize(typ: PType): BiggestInt = +proc getSize*(typ: PType): BiggestInt = result = computeSize(typ) if result < 0: internalError("getSize: " & $typ.kind) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index b9bbba551e..12fc455bd8 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -250,6 +250,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result = mapTypeToBracket("ref", mRef, t, info) of tyVar: result = mapTypeToBracket("var", mVar, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) + of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info) of tyProc: if inst: result = newNodeX(nkProcTy) @@ -304,7 +305,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; of tyNot: result = mapTypeToBracket("not", mNot, t, info) of tyAnything: result = atomicType("anything", mNone) of tyInferred: internalAssert false - of tyStatic, tyFromExpr, tyFieldAccessor: + of tyStatic, tyFromExpr: if inst: if t.n != nil: result = t.n.copyTree else: result = atomicType("void", mVoid) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 28b3dd7ab9..8c70d2b476 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -97,7 +97,7 @@ type ntyError, ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst, ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot, - ntyAnything, ntyStatic, ntyFromExpr, ntyFieldAccessor, ntyVoid + ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid TNimTypeKinds* {.deprecated.} = set[NimTypeKind] NimSymKind* = enum From f8b82869594e8009ddd7262b281eb598a2e13ab6 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 24 Sep 2017 16:02:25 +0100 Subject: [PATCH 28/56] Added changelog.md. --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.md diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000000..3c00481995 --- /dev/null +++ b/changelog.md @@ -0,0 +1,3 @@ +## v0.18.0 - dd/mm/yyyy + + From f7f3a25be8614ca046166194d19c6941b423a941 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 Sep 2017 13:19:36 +0200 Subject: [PATCH 29/56] first steps of making 'opt' a first class type for Nim --- compiler/ast.nim | 2 +- compiler/ccgtrav.nim | 3 +- compiler/ccgtypes.nim | 72 ++++++++++++++++++++++++++++++++++++--- compiler/ccgutils.nim | 2 +- compiler/condsyms.nim | 1 + compiler/jsgen.nim | 2 +- compiler/semasgn.nim | 2 +- compiler/semtypes.nim | 1 + compiler/types.nim | 75 +++++++++++++++++++++++++++++++++++++---- compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 2 ++ lib/core/typeinfo.nim | 1 + lib/system.nim | 3 ++ lib/system/assign.nim | 16 ++++++++- lib/system/channels.nim | 2 +- lib/system/deepcopy.nim | 2 +- lib/system/gc.nim | 12 +++---- lib/system/gc2.nim | 10 +++--- lib/system/gc_ms.nim | 8 ++--- lib/system/hti.nim | 15 +++++++++ 20 files changed, 198 insertions(+), 35 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 8237c161eb..0a87e96153 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -354,7 +354,7 @@ type tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, - tyUnused0, tyUnused1, tyUnused2, + tyOptAsRef, tyUnused1, tyUnused2, tyVarargs, tyUnused, tyProxy # used as errornous type (for idetools) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index fa228ff042..4215a84b1b 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -121,7 +121,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash; var c: TTraversalClosure var p = newProc(nil, m) result = "Marker_" & getTypeName(m, origTyp, sig) - let typ = origTyp.skipTypes(abstractInst) + var typ = origTyp.skipTypes(abstractInst) + if typ.kind == tyOpt: typ = optLowering(typ) case reason of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n" diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1ca001b428..254b134294 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -183,7 +183,7 @@ proc mapType(typ: PType): TCTypeKind = of 8: result = ctInt64 else: internalError("mapType") of tyRange: result = mapType(typ.sons[0]) - of tyPtr, tyVar, tyRef: + of tyPtr, tyVar, tyRef, tyOptAsRef: var base = skipTypes(typ.lastSon, typedescInst) case base.kind of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray @@ -194,6 +194,13 @@ proc mapType(typ: PType): TCTypeKind = else: result = ctPtr of tyPointer: result = ctPtr of tySequence: result = ctNimSeq + of tyOpt: + case optKind(typ) + of oBool: result = ctStruct + of oNil, oPtr: result = ctPtr + of oEnum: + # The 'nil' value is always negative, so we always use a signed integer + result = if getSize(typ.sons[0]) == 8: ctInt64 else: ctInt32 of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct of tyString: result = ctNimStr of tyCString: result = ctCString @@ -350,7 +357,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = if result != nil: return result = getTypePre(m, typ, sig) if result != nil: return - let concrete = typ.skipTypes(abstractInst) + let concrete = typ.skipTypes(abstractInst + {tyOpt}) case concrete.kind of tySequence, tyTuple, tyObject: result = getTypeName(m, typ, sig) @@ -376,6 +383,12 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = of tySequence: result = getTypeForward(m, t, hashType(t)) & "*" pushType(m, t) + of tyOpt: + if optKind(etB) == oPtr: + result = getTypeForward(m, t, hashType(t)) & "*" + pushType(m, t) + else: + result = getTypeDescAux(m, t, check) else: result = getTypeDescAux(m, t, check) @@ -506,7 +519,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: addf(result, "$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check), sname]) - elif fieldType.kind == tySequence: + elif fieldType.kind in {tySequence, tyOpt}: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) elif field.bitsize != 0: @@ -625,7 +638,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = excl(check, t.id) return case t.kind - of tyRef, tyPtr, tyVar: + of tyRef, tyOptAsRef, tyPtr, tyVar: var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and compileToCpp(m): "&" else: "*" var et = origTyp.skipTypes(abstractInst).lastSon @@ -652,6 +665,21 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = result = name & "*" & star m.typeCache[sig] = result pushType(m, et) + of tyOpt: + if etB.sons[0].kind in {tyObject, tyTuple}: + let name = getTypeForward(m, et, hashType et) + result = name & "*" & star + m.typeCache[sig] = result + pushType(m, et) + elif optKind(etB) == oBool: + let name = getTypeForward(m, et, hashType et) + result = name & "*" + m.typeCache[sig] = result + pushType(m, et) + else: + # else we have a strong dependency :-( + result = getTypeDescAux(m, et, check) & star + m.typeCache[sig] = result else: # else we have a strong dependency :-( result = getTypeDescAux(m, et, check) & star @@ -727,6 +755,38 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = else: result = rope("TGenericSeq") add(result, "*") + of tyOpt: + result = cacheGetType(m.typeCache, sig) + if result == nil: + case optKind(t) + of oBool: + result = cacheGetType(m.forwTypeCache, sig) + if result == nil: + result = getTypeName(m, origTyp, sig) + addf(m.s[cfsForwardTypes], getForwardStructFormat(m), + [structOrUnion(t), result]) + m.forwTypeCache[sig] = result + appcg(m, m.s[cfsSeqTypes], "struct $2 {$n" & + " NIM_BOOL Field0;$n" & + " $1 Field1;$n" & + "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + of oPtr: + let et = t.sons[0] + if et.kind in {tyTuple, tyObject}: + let name = getTypeForward(m, et, hashType et) + result = name & "*" + pushType(m, et) + else: + result = getTypeDescAux(m, t.sons[0], check) & "*" + of oNil: + result = getTypeDescAux(m, t.sons[0], check) + of oEnum: + result = getTypeName(m, origTyp, sig) + if getSize(t.sons[0]) == 8: + addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result]) + else: + addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result]) + m.typeCache[sig] = result of tyArray: var n: BiggestInt = lengthOrd(t) if n <= 0: n = 1 # make an array of at least one element @@ -1114,6 +1174,8 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = proc genTypeInfo(m: BModule, t: PType): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) + if t.kind == tyOpt: + return genTypeInfo(m, optLowering(t)) let sig = hashType(origType) result = m.typeInfoMarker.getOrDefault(sig) @@ -1159,7 +1221,7 @@ proc genTypeInfo(m: BModule, t: PType): Rope = else: let x = fakeClosureType(t.owner) genTupleInfo(m, x, x, result) - of tySequence, tyRef: + of tySequence, tyRef, tyOptAsRef: genTypeInfoAux(m, t, t, result) if gSelectedGC >= gcMarkAndSweep: let markerProc = genTraverseProc(m, origType, sig, tiNew) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 315efbd641..4cfeeb3c36 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -157,7 +157,7 @@ proc getUniqueType*(key: PType): PType = else: # ugh, we need the canon here: result = slowSearch(key, k) - of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("getUniqueType") + of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("getUniqueType") proc makeSingleLineCString*(s: string): string = result = "\"" diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index dc97e36489..9863e90bb5 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -107,3 +107,4 @@ proc initDefines*() = defineSymbol("nimDistros") defineSymbol("nimHasCppDefine") defineSymbol("nimGenericInOutFlags") + when false: defineSymbol("nimHasOpt") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 0f8d763c27..7b1c438172 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -202,7 +202,7 @@ proc mapType(typ: PType): TJSTypeKind = else: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString - of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapType") + of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapType") proc mapType(p: PProc; typ: PType): TJSTypeKind = if p.target == targetPHP: result = etyObject diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index b51d7167b2..dbb2a140b5 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -229,7 +229,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyVar, tyAlias: liftBodyAux(c, lastSon(t), body, x, y) - of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("liftBodyAux") + of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("liftBodyAux") proc newProcType(info: TLineInfo; owner: PSym): PType = result = newType(tyProc, owner) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c44f94db61..fbb5d0b6b5 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1393,6 +1393,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) of mSeq: result = semContainer(c, n, tySequence, "seq", prev) + of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) of mExpr: diff --git a/compiler/types.nim b/compiler/types.nim index 12c10a4a37..3dbf6fd180 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -45,8 +45,6 @@ type proc equalParams*(a, b: PNode): TParamsEquality # returns whether the parameter lists of the procs a, b are exactly the same -proc isOrdinalType*(t: PType): bool -proc enumHasHoles*(t: PType): bool const # TODO: Remove tyTypeDesc from each abstractX and (where necessary) @@ -129,7 +127,7 @@ proc elemType*(t: PType): PType = else: result = t.lastSon assert(result != nil) -proc isOrdinalType(t: PType): bool = +proc isOrdinalType*(t: PType): bool = assert(t != nil) const # caution: uint, uint64 are no ordinal types! @@ -137,7 +135,7 @@ proc isOrdinalType(t: PType): bool = parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tyDistinct} t.kind in baseKinds or (t.kind in parentKinds and isOrdinalType(t.sons[0])) -proc enumHasHoles(t: PType): bool = +proc enumHasHoles*(t: PType): bool = var b = t while b.kind in {tyRange, tyGenericInst, tyAlias}: b = b.sons[0] result = b.kind == tyEnum and tfEnumHasHoles in b.flags @@ -995,7 +993,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = cycleCheck() result = sameTypeAux(a.lastSon, b.lastSon, c) of tyNone: result = false - of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("sameFlags") + of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("sameFlags") proc sameBackendType*(x, y: PType): bool = var c = initSameTypeClosure() @@ -1176,7 +1174,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = nil - of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("typeAllowedAux") + of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("typeAllowedAux") proc typeAllowed*(t: PType, kind: TSymKind): PType = # returns 'nil' on success and otherwise the part of the type that is @@ -1187,6 +1185,63 @@ proc typeAllowed*(t: PType, kind: TSymKind): PType = proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) +type + OptKind* = enum ## What to map 'opt T' to internally. + oBool ## opt[T] requires an additional 'bool' field + oNil ## opt[T] has no overhead since 'nil' + ## is available + oEnum ## We can use some enum value that is not yet + ## used for opt[T] + oPtr ## opt[T] actually introduces a hidden pointer + ## in order for the type recursion to work + +proc optKind*(typ: PType): OptKind = + ## return true iff 'opt[T]' can be mapped to 'T' internally + ## because we have a 'nil' value available: + assert typ.kind == tyOpt + case typ.sons[0].skipTypes(abstractInst).kind + of tyRef, tyPtr, tyProc: + result = oNil + of tyArray, tyObject, tyTuple: + result = oPtr + of tyBool: result = oEnum + of tyEnum: + assert(typ.n.sons[0].kind == nkSym) + if typ.n.sons[0].sym.position != low(int): + result = oEnum + else: + result = oBool + else: + result = oBool + +proc optLowering*(typ: PType): PType = + case optKind(typ) + of oNil: result = typ.sons[0] + of oPtr: + result = newType(tyOptAsRef, typ.owner) + result.rawAddSon typ.sons[0] + of oBool: + result = newType(tyTuple, typ.owner) + result.rawAddSon newType(tyBool, typ.owner) + result.rawAddSon typ.sons[0] + of oEnum: + if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): + result = newType(tyInt32, typ.owner) + else: + result = newType(tyInt64, typ.owner) + +proc optEnumValue*(typ: PType): BiggestInt = + assert typ.kind == tyOpt + assert optKind(typ) == oEnum + let elem = typ.sons[0].skipTypes(abstractInst).kind + if elem == tyBool: + result = 2 + else: + assert elem == tyEnum + assert typ.n.sons[0].sym.position != low(int) + result = typ.n.sons[0].sym.position - 1 + + const szNonConcreteType* = -3 szIllegalRecursion* = -2 @@ -1341,6 +1396,14 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = of tyStatic: result = if typ.n != nil: computeSizeAux(typ.lastSon, a) else: szUnknownSize + of tyOpt: + case optKind(typ) + of oBool: result = computeSizeAux(lastSon(typ), a) + 1 + of oEnum: + if lastOrd(typ) + 1 < `shl`(BiggestInt(1), 32): result = 4 + else: result = 8 + of oNil: result = computeSizeAux(lastSon(typ), a) + of oPtr: result = ptrSize else: #internalError("computeSizeAux()") result = szUnknownSize diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 12fc455bd8..2b3cfeeeb4 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -314,7 +314,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add atomicType("static", mNone) if t.n != nil: result.add t.n.copyTree - of tyUnused, tyUnused0, tyUnused1, tyUnused2: internalError("mapTypeToAstX") + of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("mapTypeToAstX") proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = result = mapTypeToAstX(t, info, false, true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 6f9d704951..ee9af7de25 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1537,6 +1537,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = addSon(result, getNullValue(t.sons[i], info)) of tySet: result = newNodeIT(nkCurly, info, t) + of tyOpt: + result = newNodeIT(nkNilLit, info, t) else: globalError(info, "cannot create null element for: " & $t.kind) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 72b139202a..0f3639f0d2 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -54,6 +54,7 @@ type akUInt16 = 42, ## any represents an unsigned in16 akUInt32 = 43, ## any represents an unsigned int32 akUInt64 = 44, ## any represents an unsigned int64 + akOpt = 44+18 ## the builtin 'opt' type. Any* = object ## can represent any nim value; NOTE: the wrapped ## value can be modified with its wrapper! This means diff --git a/lib/system.nim b/lib/system.nim index 61d79bac3c..e642eb335b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -246,6 +246,9 @@ type UncheckedArray* {.unchecked.}[T] = array[0, T] ## Array with no bounds checking +when defined(nimHasOpt): + type opt*{.magic: "Opt".}[T] + proc high*[T: Ordinal](x: T): T {.magic: "High", noSideEffect.} ## returns the highest possible index of an array, a sequence, a string or ## the highest possible value of an ordinal value `x`. As a special diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 61c33e51b1..01261d4b5d 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -89,6 +89,19 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = cast[pointer](s +% i*% mt.base.size), mt.base, shallow) of tyRef: unsureAsgnRef(cast[PPointer](dest), cast[PPointer](s)[]) + of tyOptAsRef: + let s2 = cast[PPointer](src)[] + let d = cast[PPointer](dest) + if s2 == nil: + unsureAsgnRef(d, s2) + else: + when declared(usrToCell): + let realType = usrToCell(s2).typ + else: + let realType = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] + else: mt.base + var z = newObj(realType, realType.base.size) + genericAssignAux(d, addr z, mt.base, shallow) else: copyMem(dest, src, mt.size) # copy raw bits @@ -115,6 +128,7 @@ when false: of tyPtr: k = "ptr" of tyRef: k = "ref" of tyVar: k = "var" + of tyOptAsRef: k = "optref" of tySequence: k = "seq" of tyProc: k = "proc" of tyPointer: k = "range" @@ -195,7 +209,7 @@ proc genericReset(dest: pointer, mt: PNimType) = var d = cast[ByteAddress](dest) sysAssert(mt != nil, "genericReset 2") case mt.kind - of tyString, tyRef, tySequence: + of tyString, tyRef, tyOptAsRef, tySequence: unsureAsgnRef(cast[PPointer](dest), nil) of tyTuple: genericResetAux(dest, mt.node) diff --git a/lib/system/channels.nim b/lib/system/channels.nim index 1b90e245f1..df6c6d41e1 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -144,7 +144,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, for i in 0..(mt.size div mt.base.size)-1: storeAux(cast[pointer](d +% i*% mt.base.size), cast[pointer](s +% i*% mt.base.size), mt.base, t, mode) - of tyRef: + of tyRef, tyOptAsRef: var s = cast[PPointer](src)[] var x = cast[PPointer](dest) if s == nil: diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 65ba2278c8..51e138e5e1 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -124,7 +124,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = for i in 0..(mt.size div mt.base.size)-1: genericDeepCopyAux(cast[pointer](d +% i*% mt.base.size), cast[pointer](s +% i*% mt.base.size), mt.base, tab) - of tyRef: + of tyRef, tyOptAsRef: let s2 = cast[PPointer](src)[] if s2 == nil: unsureAsgnRef(cast[PPointer](dest), s2) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 80aa5cf1be..13778ee5eb 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -349,7 +349,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = for i in 0..n.len-1: # inlined for speed if n.sons[i].kind == nkSlot: - if n.sons[i].typ.kind in {tyRef, tyString, tySequence}: + if n.sons[i].typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}: doOperation(cast[PPointer](d +% n.sons[i].offset)[], op) else: forAllChildrenAux(cast[pointer](d +% n.sons[i].offset), @@ -366,7 +366,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind - of tyRef, tyString, tySequence: # leaf: + of tyRef, tyOptAsRef, tyString, tySequence: # leaf: doOperation(cast[PPointer](d)[], op) of tyObject, tyTuple: forAllSlotsAux(dest, mt.node, op) @@ -379,13 +379,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) = gcAssert(cell != nil, "forAllChildren: 1") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2") gcAssert(cell.typ != nil, "forAllChildren: 3") - gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4" + gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4" let marker = cell.typ.marker if marker != nil: marker(cellToUsr(cell), op.int) else: case cell.typ.kind - of tyRef: # common case + of tyRef, tyOptAsRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: var d = cast[ByteAddress](cellToUsr(cell)) @@ -461,7 +461,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = incTypeSize typ, size sysAssert(allocInv(gch.region), "rawNewObj begin") acquire(gch) - gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") + gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small" @@ -509,7 +509,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = incTypeSize typ, size sysAssert(allocInv(gch.region), "newObjRC1 begin") acquire(gch) - gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") + gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 68da65727f..55d515d0be 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -358,7 +358,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind - of tyRef, tyString, tySequence: # leaf: + of tyRef, tyOptAsRef, tyString, tySequence: # leaf: doOperation(cast[PPointer](d)[], op) of tyObject, tyTuple: forAllSlotsAux(dest, mt.node, op) @@ -371,13 +371,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) = gcAssert(cell != nil, "forAllChildren: 1") gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2") gcAssert(cell.typ != nil, "forAllChildren: 3") - gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4" + gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 4" let marker = cell.typ.marker if marker != nil: marker(cellToUsr(cell), op.int) else: case cell.typ.kind - of tyRef: # common case + of tyRef, tyOptAsRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: var d = cast[ByteAddress](cellToUsr(cell)) @@ -442,7 +442,7 @@ proc gcInvariant*() = proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 sysAssert(allocInv(gch.region), "rawNewObj begin") - gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") + gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") @@ -487,7 +487,7 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = # generates a new object and sets its reference counter to 1 sysAssert(allocInv(gch.region), "newObjRC1 begin") - gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") + gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index e03140d05f..494c32b1a7 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -252,7 +252,7 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind - of tyRef, tyString, tySequence: # leaf: + of tyRef, tyOptAsRef, tyString, tySequence: # leaf: doOperation(cast[PPointer](d)[], op) of tyObject, tyTuple: forAllSlotsAux(dest, mt.node, op) @@ -264,13 +264,13 @@ proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = proc forAllChildren(cell: PCell, op: WalkOp) = gcAssert(cell != nil, "forAllChildren: 1") gcAssert(cell.typ != nil, "forAllChildren: 2") - gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 3" + gcAssert cell.typ.kind in {tyRef, tyOptAsRef, tySequence, tyString}, "forAllChildren: 3" let marker = cell.typ.marker if marker != nil: marker(cellToUsr(cell), op.int) else: case cell.typ.kind - of tyRef: # common case + of tyRef, tyOptAsRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: var d = cast[ByteAddress](cellToUsr(cell)) @@ -285,7 +285,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 incTypeSize typ, size acquire(gch) - gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") + gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") diff --git a/lib/system/hti.nim b/lib/system/hti.nim index 69f4f95083..45b1d1cd37 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -62,6 +62,21 @@ type tyUInt16, tyUInt32, tyUInt64, + tyOptAsRef, tyUnused1, tyUnused2, + tyVarargsHidden, + tyUnusedHidden, + tyProxyHidden, + tyBuiltInTypeClassHidden, + tyUserTypeClassHidden, + tyUserTypeClassInstHidden, + tyCompositeTypeClassHidden, + tyInferredHidden, + tyAndHidden, tyOrHidden, tyNotHidden, + tyAnythingHidden, + tyStaticHidden, + tyFromExprHidden, + tyOpt, + tyVoidHidden TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase TNimNode {.codegenType.} = object From 77453f33df5f464e2bee589d6991435abb6ca3d3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 Sep 2017 14:27:19 +0200 Subject: [PATCH 30/56] fixes #6435 --- compiler/ccgcalls.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index e2e52839ab..a00e2bc779 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -519,7 +519,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = if d.k == locNone: getTemp(p, typ.sons[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc - initLoc(list, locCall, nil, OnUnknown) + initLoc(list, locCall, ri, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: From d3dc753732562e88aafad849bb885eb4f5b23c6f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 Sep 2017 14:52:52 +0200 Subject: [PATCH 31/56] make the tester compile again --- lib/core/typeinfo.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 0f3639f0d2..16580b3183 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -54,7 +54,7 @@ type akUInt16 = 42, ## any represents an unsigned in16 akUInt32 = 43, ## any represents an unsigned int32 akUInt64 = 44, ## any represents an unsigned int64 - akOpt = 44+18 ## the builtin 'opt' type. +# akOpt = 44+18 ## the builtin 'opt' type. Any* = object ## can represent any nim value; NOTE: the wrapped ## value can be modified with its wrapper! This means From eeb2661a28069c94b44cf965817709b0cd9886d7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 Sep 2017 15:22:07 +0200 Subject: [PATCH 32/56] fixes #6433 --- lib/system/assign.nim | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 01261d4b5d..115df61a75 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -63,12 +63,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = sysAssert(dest != nil, "genericAssignAux 3") unsureAsgnRef(x, newSeq(mt, seq.len)) var dst = cast[ByteAddress](cast[PPointer](dest)[]) - for i in 0..seq.len-1: - genericAssignAux( - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), - cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% - GenericSeqSize), - mt.base, shallow) + if ntfNoRefs in mt.base.flags: + copyMem(cast[pointer](dst +% GenericSeqSize), + cast[pointer](cast[ByteAddress](s2) +% GenericSeqSize), + seq.len * mt.base.size) + else: + for i in 0..seq.len-1: + genericAssignAux( + cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% + GenericSeqSize), + mt.base, shallow) of tyObject: if mt.base != nil: genericAssignAux(dest, src, mt.base, shallow) From 61720e0df9475af647de171f6b2964c36234961a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 26 Sep 2017 10:24:14 +0200 Subject: [PATCH 33/56] breaking change: getAst strips away pointless nnkStmtList nodes; node rendering of nnkStmtList without wrong indentation --- compiler/renderer.nim | 8 +++++++- compiler/semexprs.nim | 7 ++++--- compiler/vm.nim | 3 ++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c52161a557..05329394ac 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1366,7 +1366,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = proc renderTree(n: PNode, renderFlags: TRenderFlags = {}): string = var g: TSrcGen initSrcGen(g, renderFlags) - gsub(g, n) + # do not indent the initial statement list so that + # writeFile("file.nim", repr n) + # produces working Nim code: + if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: + gstmts(g, n, emptyContext, doIndent = false) + else: + gsub(g, n) result = g.buf proc renderModule(n: PNode, filename: string, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fb86eae05c..439f2772a3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1617,9 +1617,10 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = # Preserve the magic symbol in order to be handled in evals.nim internalAssert n.sons[0].sym.magic == mExpandToAst #n.typ = getSysSym("NimNode").typ # expandedSym.getReturnType - n.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode" - else: sysTypeFromName"PNimrodNode" - result = n + if n.kind == nkStmtList and n.len == 1: result = n[0] + else: result = n + result.typ = if getCompilerProc("NimNode") != nil: sysTypeFromName"NimNode" + else: sysTypeFromName"PNimrodNode" proc semExpandToAst(c: PContext, n: PNode, magicSym: PSym, flags: TExprFlags = {}): PNode = diff --git a/compiler/vm.nim b/compiler/vm.nim index 8d4359db93..cb801bd5ee 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -979,7 +979,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] macroCall.add(node) - let a = evalTemplate(macroCall, prc, genSymOwner) + var a = evalTemplate(macroCall, prc, genSymOwner) + if a.kind == nkStmtList and a.len == 1: a = a[0] a.recSetFlagIsRef ensureKind(rkNode) regs[ra].node = a From 2bdbe97af1cd9046ba8028c5530e75025561afcc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 26 Sep 2017 22:19:31 +0200 Subject: [PATCH 34/56] workaround a GC/codegen issue that proves nasty to fix --- compiler/vm.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index cb801bd5ee..79bfb911de 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -66,7 +66,10 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] # we now use the same format as in system/except.nim - var s = toFilename(info) + var s = substr(toFilename(info), 0) + # this 'substr' prevents a strange corruption. XXX This needs to be + # investigated eventually but first attempts to fix it broke everything + # see the araq-wip-fixed-writebarrier branch. var line = toLinenumber(info) if line > 0: add(s, '(') From 414e7eff0450d45de9c91f724c021d987b08c693 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 26 Sep 2017 23:07:37 +0200 Subject: [PATCH 35/56] make tests green again --- lib/js/jsffi.nim | 6 +++--- tests/vm/tnimnode.nim | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 0d359d9e67..13eb1e759e 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -300,7 +300,7 @@ macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V], result = quote do: (`dotOp`(`obj`, `field`))() for elem in args: - result[0].add elem + result.add elem # Iterators: @@ -471,7 +471,7 @@ macro bindMethod*(procedure: typed): auto = # construct the `this` parameter: thisQuote = quote do: var `this` {. nodecl, importc .} : `thisType` - call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0][0]) + call = newNimNode(nnkCall).add(rawProc[0], thisQuote[0][0][0]) # construct the procedure call inside the method if args.len > 2: for idx in 2..args.len-1: @@ -483,6 +483,6 @@ macro bindMethod*(procedure: typed): auto = params, rawProc[4], rawProc[5], - newTree(nnkStmtList, thisQuote[0], call) + newTree(nnkStmtList, thisQuote, call) ) result = body diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index e5a41e3c2a..60e3189b05 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -15,9 +15,9 @@ static: proc checkNode(arg: NimNode; name: string): void {. compileTime .} = echo "checking ", name - + assertEq arg.lispRepr , "StmtList(DiscardStmt(Empty()))" - + node = arg nodeArray = [arg] nodeSeq[0] = arg @@ -38,9 +38,9 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = static: # the root node that is used to generate the Ast var stmtList: NimNode - + stmtList = newStmtList(nnkDiscardStmt.newTree(newEmptyNode())) - + checkNode(stmtList, "direct construction") @@ -50,12 +50,12 @@ macro foo(stmtList: untyped): untyped = foo: discard - + static: stmtList = quote do: discard - checkNode(stmtList, "create with quote") + checkNode(newTree(nnkStmtList, stmtList), "create with quote") static: @@ -64,13 +64,13 @@ static: for i in 0 ..< 10: discard - let innerBody = loop[0][2] + let innerBody = loop[2] innerBody.add newCall(ident"echo", newLit("Hello World")) - assertEq loop[0][2].lispRepr, innerBody.lispRepr + assertEq loop[2].lispRepr, innerBody.lispRepr echo "OK" - + static: echo "testing creation of comment node" From 7083c8df578217f990a866d646caa2b26ccf520a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 27 Sep 2017 12:00:19 +0200 Subject: [PATCH 36/56] minor improvement for the db_sqlite module --- lib/impure/db_sqlite.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 1633d48f73..53dafdda76 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -129,7 +129,8 @@ proc tryExec*(db: DbConn, query: SqlQuery, var q = dbFormat(query, args) var stmt: sqlite3.Pstmt if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: - if step(stmt) == SQLITE_DONE: + let x = step(stmt) + if x in {SQLITE_DONE, SQLITE_ROW}: result = finalize(stmt) == SQLITE_OK proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. From 7abad507c956b7eb668217ffa89a5c0ab5391a8d Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Wed, 27 Sep 2017 16:55:55 -0400 Subject: [PATCH 37/56] Add flatMap operator to Options (#6404) * Add >>= operator to Options * options.bind callback signature: A -> Option[B] * Use `flatMap` as the name of the Option bind operation. * Rename Options test "bind" to "flatMap" * CR from @dom96: Remove spaces inside of check() call --- lib/pure/options.nim | 79 +++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 2abb800166..ad63bbcb6e 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -15,7 +15,7 @@ ## A value of type ``Option[T]`` either contains a value `x` (represented as ## ``some(x)``) or is empty (``none(T)``). ## -## This can be useful when you have a value that can be present or not. The +## This can be useful when you have a value that can be present or not. The ## absence of a value is often represented by ``nil``, but it is not always ## available, nor is it always a good solution. ## @@ -67,10 +67,8 @@ ## assert(false) # This will not be reached ## except UnpackError: # Because an exception is raised ## discard - import typetraits - type Option*[T] = object ## An optional type that stores its value and state separately in a boolean. @@ -78,7 +76,6 @@ type has: bool UnpackError* = ref object of ValueError - proc some*[T](val: T): Option[T] = ## Returns a ``Option`` that has this value. result.has = true @@ -88,14 +85,12 @@ proc none*(T: typedesc): Option[T] = ## Returns a ``Option`` for this type that has no value. result.has = false - proc isSome*[T](self: Option[T]): bool = self.has proc isNone*[T](self: Option[T]): bool = not self.has - proc unsafeGet*[T](self: Option[T]): T = ## Returns the value of a ``some``. Behavior is undefined for ``none``. assert self.isSome @@ -110,12 +105,11 @@ proc get*[T](self: Option[T]): T = proc get*[T](self: Option[T], otherwise: T): T = ## Returns the contents of this option or `otherwise` if the option is none. - if self.isSome: + if self.has: self.val else: otherwise - proc map*[T](self: Option[T], callback: proc (input: T)) = ## Applies a callback to the value in this Option if self.has: @@ -123,12 +117,27 @@ proc map*[T](self: Option[T], callback: proc (input: T)) = proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] = ## Applies a callback to the value in this Option and returns an option - ## containing the new value. If this option is None, None will be returned + ## containing the new value. If this option is None, None will be returned. if self.has: - some[R]( callback(self.val) ) + some[R](callback(self.val)) else: none(R) +proc flatten*[A](self: Option[Option[A]]): Option[A] = + ## Remove one level of structure in a nested Option. + if self.has: + self.val + else: + none(A) + +proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Option[B] = + ## Applies a callback to the value in this Option and returns an + ## option containing the new value. If this option is None, None will be + ## returned. Similar to ``map``, with the difference that the callback + ## returns an Option, not a raw value. This allows multiple procs with a + ## signature of ``A -> Option[B]`` (including A = B) to be chained together. + map(self, callback).flatten() + proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = ## Applies a callback to the value in this Option. If the callback returns ## `true`, the option is returned as a Some. If it returns false, it is @@ -138,21 +147,18 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = else: self - proc `==`*(a, b: Option): bool = ## Returns ``true`` if both ``Option``s are ``none``, ## or if they have equal values (a.has and b.has and a.val == b.val) or (not a.has and not b.has) - -proc `$`*[T]( self: Option[T] ): string = +proc `$`*[T](self: Option[T]): string = ## Returns the contents of this option or `otherwise` if the option is none. if self.has: "Some(" & $self.val & ")" else: "None[" & T.name & "]" - when isMainModule: import unittest, sequtils @@ -198,12 +204,12 @@ when isMainModule: check false test "get with a default value": - check( some("Correct").get("Wrong") == "Correct" ) - check( stringNone.get("Correct") == "Correct" ) + check(some("Correct").get("Wrong") == "Correct") + check(stringNone.get("Correct") == "Correct") test "$": - check( $(some("Correct")) == "Some(Correct)" ) - check( $(stringNone) == "None[string]" ) + check($(some("Correct")) == "Some(Correct)") + check($(stringNone) == "None[string]") test "map with a void result": var procRan = 0 @@ -212,11 +218,38 @@ when isMainModule: intNone.map(proc (v: int) = check false) test "map": - check( some(123).map(proc (v: int): int = v * 2) == some(246) ) - check( intNone.map(proc (v: int): int = v * 2).isNone ) + check(some(123).map(proc (v: int): int = v * 2) == some(246)) + check(intNone.map(proc (v: int): int = v * 2).isNone) test "filter": - check( some(123).filter(proc (v: int): bool = v == 123) == some(123) ) - check( some(456).filter(proc (v: int): bool = v == 123).isNone ) - check( intNone.filter(proc (v: int): bool = check false).isNone ) + check(some(123).filter(proc (v: int): bool = v == 123) == some(123)) + check(some(456).filter(proc (v: int): bool = v == 123).isNone) + check(intNone.filter(proc (v: int): bool = check false).isNone) + test "flatMap": + proc addOneIfNotZero(v: int): Option[int] = + if v != 0: + result = some(v + 1) + else: + result = none(int) + + check(some(1).flatMap(addOneIfNotZero) == some(2)) + check(some(0).flatMap(addOneIfNotZero) == none(int)) + check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3)) + + proc maybeToString(v: int): Option[string] = + if v != 0: + result = some($v) + else: + result = none(string) + + check(some(1).flatMap(maybeToString) == some("1")) + + proc maybeExclaim(v: string): Option[string] = + if v != "": + result = some v & "!" + else: + result = none(string) + + check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!")) + check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string)) From 455452e107e13f06b6d5921f5503c244f905a63e Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Thu, 28 Sep 2017 09:51:51 +0300 Subject: [PATCH 38/56] Fix node quit (#6444) --- lib/system.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index e642eb335b..dc3152faf8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1443,6 +1443,10 @@ elif defined(genode): importcpp: "genodeEnv->parent().exit(@); Genode::sleep_forever()", header: "".} +elif defined(nodejs): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", + importc: "process.exit", noreturn.} + else: proc quit*(errorcode: int = QuitSuccess) {. magic: "Exit", importc: "exit", header: "", noreturn.} From 5ae82708aabb8c127aaa1694e384919dd7ac229a Mon Sep 17 00:00:00 2001 From: konqoro Date: Thu, 28 Sep 2017 11:35:19 +0300 Subject: [PATCH 39/56] Add default value for filename in parseJson proc (#6441) --- lib/pure/json.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index f02bdbf084..656114fb1c 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1214,7 +1214,7 @@ proc parseJson(p: var JsonParser): JsonNode = raiseParseErr(p, "{") when not defined(js): - proc parseJson*(s: Stream, filename: string): JsonNode = + proc parseJson*(s: Stream, filename: string = ""): JsonNode = ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed ## for nice error messages. ## If `s` contains extra data, it will raise `JsonParsingError`. From d453bc1d4dc2c56215be34967afdca3a33b63042 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 28 Sep 2017 10:35:45 +0200 Subject: [PATCH 40/56] fixes #6438 --- lib/pure/json.nim | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 73c4f97f45..f02bdbf084 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -769,6 +769,19 @@ proc `%`*[T](elements: openArray[T]): JsonNode = result = newJArray() for elem in elements: result.add(%elem) +when false: + # For 'consistency' we could do this, but that only pushes people further + # into that evil comfort zone where they can use Nim without understanding it + # causing problems later on. + proc `%`*(elements: set[bool]): JsonNode = + ## Generic constructor for JSON data. Creates a new `JObject JsonNode`. + ## This can only be used with the empty set ``{}`` and is supported + ## to prevent the gotcha ``%*{}`` which used to produce an empty + ## JSON array. + result = newJObject() + assert false notin elements, "usage error: only empty sets allowed" + assert true notin elements, "usage error: only empty sets allowed" + proc `%`*(o: object): JsonNode = ## Generic constructor for JSON data. Creates a new `JObject JsonNode` result = newJObject() @@ -789,27 +802,25 @@ proc `%`*(o: enum): JsonNode = proc toJson(x: NimNode): NimNode {.compiletime.} = case x.kind of nnkBracket: # array + if x.len == 0: return newCall(bindSym"newJArray") result = newNimNode(nnkBracket) for i in 0 .. Date: Thu, 28 Sep 2017 21:48:43 +0200 Subject: [PATCH 41/56] GC v2 works sometimes --- compiler/nim.nim | 2 + lib/system/gc2.nim | 494 +++++++++++---------------------------------- 2 files changed, 125 insertions(+), 371 deletions(-) diff --git a/compiler/nim.nim b/compiler/nim.nim index 56885e9f1b..89225a5e01 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -119,4 +119,6 @@ condsyms.initDefines() when not defined(selftest): handleCmdLine(newIdentCache(), newConfigRef()) + when declared(GC_setMaxPause): + echo GC_getStatistics() msgQuit(int8(msgs.gErrorCounter > 0)) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 55d515d0be..6dffc323e9 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf +# (c) Copyright 2017 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,17 +9,19 @@ # Garbage Collector # -# The basic algorithm is *Deferred Reference Counting* with an incremental mark +# The basic algorithm is an incremental mark # and sweep GC to free cycles. It is hard realtime in that if you play # according to its rules, no deadline will ever be missed. - -# XXX Ensure by smart color masking that the object is not in the ZCT. +# Since this kind of collector is very bad at recycling dead objects +# early, Nim's codegen emits ``nimEscape`` calls at strategic +# places. For this to work even 'unsureAsgnRef' needs to mark things +# so that only return values need to be considered in ``nimEscape``. {.push profiler:off.} const CycleIncrease = 2 # is a multiplicative increase - InitialCycleThreshold = 4*1024*1024 # X MB because cycle checking is slow + InitialCycleThreshold = 512*1024 # start collecting after 500KB ZctThreshold = 500 # we collect garbage if the ZCT's size # reaches this threshold # this seems to be a good value @@ -40,13 +42,11 @@ type iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc) const - rcIncrement = 0b1000 # so that lowest 3 bits are not touched + escapedBit = 0b1000 # so that lowest 3 bits are not touched rcBlackOrig = 0b000 rcWhiteOrig = 0b001 rcGrey = 0b010 # traditional color for incremental mark&sweep rcUnused = 0b011 - ZctFlag = 0b100 # in ZCT - rcShift = 3 # shift by rcShift to get the reference counter colorMask = 0b011 type WalkOp = enum @@ -63,7 +63,7 @@ type GcStat = object stackScans: int # number of performed stack scans (for statistics) - cycleCollections: int # number of performed full collections + completedCollections: int # number of performed full collections maxThreshold: int # max threshold that has been set maxStackSize: int # max stack size maxStackCells: int # max stack cells in ``decStack`` @@ -93,15 +93,13 @@ type cycleThreshold: int when useCellIds: idGenerator: int - zct: CellSeq # the zero count table - decStack: CellSeq # cells in the stack that are to decref again greyStack: CellSeq recGcLock: int # prevent recursion via finalizers; no thread lock when withRealTime: maxPause: Nanos # max allowed pause in nanoseconds; active if > 0 region: MemRegion # garbage collected region stat: GcStat - additionalRoots: CellSeq # dummy roots for GC_ref/unref + additionalRoots: CellSeq # explicit roots for GC_ref/unref spaceIter: ObjectSpaceIter pDumpHeapFile: pointer # File that is used for GC_dumpHeap when hasThreadSupport: @@ -113,19 +111,25 @@ var when not defined(useNimRtl): instantiateForRegion(gch.region) +template acquire(gch: GcHeap) = + when hasThreadSupport and hasSharedHeap: + acquireSys(HeapLock) + +template release(gch: GcHeap) = + when hasThreadSupport and hasSharedHeap: + releaseSys(HeapLock) + proc initGC() = when not defined(useNimRtl): gch.red = (1-gch.black) gch.cycleThreshold = InitialCycleThreshold gch.stat.stackScans = 0 - gch.stat.cycleCollections = 0 + gch.stat.completedCollections = 0 gch.stat.maxThreshold = 0 gch.stat.maxStackSize = 0 gch.stat.maxStackCells = 0 gch.stat.cycleTableSize = 0 # init the rt - init(gch.zct) - init(gch.decStack) init(gch.additionalRoots) init(gch.greyStack) when hasThreadSupport: @@ -147,11 +151,6 @@ template gcAssert(cond: bool, msg: string) = writeStackTrace() quit 1 -proc addZCT(s: var CellSeq, c: PCell) {.noinline.} = - if (c.refcount and ZctFlag) == 0: - c.refcount = c.refcount or ZctFlag - add(s, c) - proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) @@ -168,7 +167,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} = result = usrToCell(c).typ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} = - result = int(usrToCell(p).refcount) shr rcShift + result = 0 # this that has to equals zero, otherwise we have to round up UnitsPerPage: when BitsPerPage mod (sizeof(int)*8) != 0: @@ -178,6 +177,12 @@ template color(c): expr = c.refCount and colorMask template setColor(c, col) = c.refcount = c.refcount and not colorMask or col +template markAsEscaped(c: PCell) = + c.refcount = c.refcount or escapedBit + +template didEscape(c: PCell): bool = + (c.refCount and escapedBit) != 0 + proc writeCell(file: File; msg: cstring, c: PCell) = var kind = -1 if c.typ != nil: kind = ord(c.typ.kind) @@ -189,18 +194,18 @@ proc writeCell(file: File; msg: cstring, c: PCell) = else: let id = c when leakDetector: - c_fprintf(file, "%s %p %d rc=%ld color=%c from %s(%ld)\n", - msg, id, kind, c.refcount shr rcShift, col, c.filename, c.line) + c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n", + msg, id, kind, didEscape(c), col, c.filename, c.line) else: - c_fprintf(file, "%s %p %d rc=%ld color=%c\n", - msg, id, kind, c.refcount shr rcShift, col) + c_fprintf(file, "%s %p %d escaped=%ld color=%c\n", + msg, id, kind, didEscape(c), col) proc writeCell(msg: cstring, c: PCell) = stdout.writeCell(msg, c) proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.} -template gcTrace(cell, state: expr): stmt {.immediate.} = +template gcTrace(cell, state: untyped) = when traceGC: writeCell(myastToStr(state), cell) # forward declarations: @@ -211,44 +216,17 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} # we need the prototype here for debugging purposes -when hasThreadSupport and hasSharedHeap: - template `--`(x: expr): expr = atomicDec(x, rcIncrement) <% rcIncrement - template `++`(x: expr): stmt = discard atomicInc(x, rcIncrement) -else: - template `--`(x: expr): expr = - dec(x, rcIncrement) - x <% rcIncrement - template `++`(x: expr): stmt = inc(x, rcIncrement) - proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! discard -proc rtlAddZCT(c: PCell) {.rtl, inl.} = - # we MUST access gch as a global here, because this crosses DLL boundaries! - addZCT(gch.zct, c) - -proc decRef(c: PCell) {.inline.} = - gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr") - gcAssert(c.refcount >=% rcIncrement, "decRef") - if --c.refcount: - rtlAddZCT(c) - -proc incRef(c: PCell) {.inline.} = - gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr") - c.refcount = c.refcount +% rcIncrement - -proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = - decRef(usrToCell(p)) - proc nimGCref(p: pointer) {.compilerProc.} = let cell = usrToCell(p) - incRef(cell) + markAsEscaped(cell) add(gch.additionalRoots, cell) proc nimGCunref(p: pointer) {.compilerProc.} = let cell = usrToCell(p) - decRef(cell) var L = gch.additionalRoots.len-1 var i = L let d = gch.additionalRoots.d @@ -259,6 +237,12 @@ proc nimGCunref(p: pointer) {.compilerProc.} = break dec(i) +proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = + discard "can we do some freeing here?" + +proc nimGCunrefRC1(p: pointer) {.compilerProc, inline.} = + discard "can we do some freeing here?" + template markGrey(x: PCell) = if x.color != 1-gch.black and gch.phase == Phase.Marking: if not isAllocatedPtr(gch.region, x): @@ -272,59 +256,32 @@ proc GC_addCycleRoot*[T](p: ref T) {.inline.} = ## adds 'p' to the cycle candidate set for the cycle collector. It is ## necessary if you used the 'acyclic' pragma for optimization ## purposes and need to break cycles manually. - rtlAddCycleRoot(usrToCell(cast[pointer](p))) + discard -proc nimGCunrefNoCycle(p: pointer) {.compilerProc, inline.} = - sysAssert(allocInv(gch.region), "begin nimGCunrefNoCycle") - var c = usrToCell(p) - gcAssert(isAllocatedPtr(gch.region, c), "nimGCunrefNoCycle: isAllocatedPtr") - if --c.refcount: - rtlAddZCT(c) - sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 2") - sysAssert(allocInv(gch.region), "end nimGCunrefNoCycle 5") - -proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc! +template asgnRefImpl = gcAssert(not isOnStack(dest), "asgnRef") # BUGFIX: first incRef then decRef! if src != nil: let s = usrToCell(src) - incRef(s) + markAsEscaped(s) markGrey(s) - if dest[] != nil: decRef(usrToCell(dest[])) dest[] = src +proc asgnRef(dest: PPointer, src: pointer) {.compilerProc, inline.} = + # the code generator calls this proc! + asgnRefImpl() + proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerProc, inline.} = - # the code generator calls this proc if it is known at compile time that no - # cycle is possible. - gcAssert(not isOnStack(dest), "asgnRefNoCycle") - if src != nil: - var c = usrToCell(src) - ++c.refcount - markGrey(c) - if dest[] != nil: - var c = usrToCell(dest[]) - if --c.refcount: - rtlAddZCT(c) - dest[] = src + asgnRefImpl() proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerProc.} = - # unsureAsgnRef updates the reference counters only if dest is not on the + # unsureAsgnRef marks 'src' as grey only if dest is not on the # stack. It is used by the code generator if it cannot decide wether a # reference is in the stack or not (this can happen for var parameters). - if not isOnStack(dest): - if src != nil: - let s = usrToCell(src) - incRef(s) - markGrey(s) - # XXX finally use assembler for the stack checking instead! - # the test for '!= nil' is correct, but I got tired of the segfaults - # resulting from the crappy stack checking: - if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[])) - else: - # can't be an interior pointer if it's a stack location! - gcAssert(interiorAllocatedPtr(gch.region, dest) == nil, - "stack loc AND interior pointer") + if src != nil: + let s = usrToCell(src) + markAsEscaped(s) + if not isOnStack(dest): markGrey(s) dest[] = src type @@ -388,50 +345,6 @@ proc forAllChildren(cell: PCell, op: WalkOp) = GenericSeqSize), cell.typ.base, op) else: discard -proc addNewObjToZCT(res: PCell, gch: var GcHeap) {.inline.} = - # we check the last 8 entries (cache line) for a slot that could be reused. - # In 63% of all cases we succeed here! But we have to optimize the heck - # out of this small linear search so that ``newObj`` is not slowed down. - # - # Slots to try cache hit - # 1 32% - # 4 59% - # 8 63% - # 16 66% - # all slots 68% - var L = gch.zct.len - var d = gch.zct.d - when true: - # loop unrolled for performance: - template replaceZctEntry(i: expr) = - c = d[i] - if c.refcount >=% rcIncrement: - c.refcount = c.refcount and not ZctFlag - d[i] = res - return - if L > 8: - var c: PCell - replaceZctEntry(L-1) - replaceZctEntry(L-2) - replaceZctEntry(L-3) - replaceZctEntry(L-4) - replaceZctEntry(L-5) - replaceZctEntry(L-6) - replaceZctEntry(L-7) - replaceZctEntry(L-8) - add(gch.zct, res) - else: - d[L] = res - inc(gch.zct.len) - else: - for i in countdown(L-1, max(0, L-8)): - var c = d[i] - if c.refcount >=% rcIncrement: - c.refcount = c.refcount and not ZctFlag - d[i] = res - return - add(gch.zct, res) - {.push stackTrace: off, profiler:off.} proc gcInvariant*() = sysAssert(allocInv(gch.region), "injected") @@ -439,6 +352,8 @@ proc gcInvariant*() = markForDebug(gch) {.pop.} +include gc_common + proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = # generates a new object and sets its reference counter to 0 sysAssert(allocInv(gch.region), "rawNewObj begin") @@ -453,10 +368,8 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = res.filename = framePtr.prev.filename res.line = framePtr.prev.line # refcount is zero, color is black, but mark it to be in the ZCT - res.refcount = ZctFlag or allocColor() + res.refcount = allocColor() sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") - # its refcount is zero, so add it to the ZCT: - addNewObjToZCT(res, gch) when logGC: writeCell("new cell", res) gcTrace(res, csAllocated) when useCellIds: @@ -485,95 +398,38 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = when defined(memProfiler): nimProfile(size) proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = - # generates a new object and sets its reference counter to 1 - sysAssert(allocInv(gch.region), "newObjRC1 begin") - gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") - collectCT(gch) - sysAssert(allocInv(gch.region), "newObjRC1 after collectCT") - - var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) - sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") - sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") - # now it is buffered in the ZCT - res.typ = typ - when leakDetector: - if framePtr != nil and framePtr.prev != nil: - res.filename = framePtr.prev.filename - res.line = framePtr.prev.line - res.refcount = rcIncrement or allocColor() # refcount is 1 - sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3") - when logGC: writeCell("new cell", res) - gcTrace(res, csAllocated) - when useCellIds: - inc gch.idGenerator - res.id = gch.idGenerator - result = cellToUsr(res) - zeroMem(result, size) - sysAssert(allocInv(gch.region), "newObjRC1 end") - when defined(memProfiler): nimProfile(size) + result = newObj(typ, size) proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = - let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = newObjRC1(typ, size) - cast[PGenericSeq](result).len = len - cast[PGenericSeq](result).reserved = len - when defined(memProfiler): nimProfile(size) + result = newSeq(typ, len) proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = + acquire(gch) collectCT(gch) var ol = usrToCell(old) - gcAssert(isAllocatedPtr(gch.region, ol), "growObj: freed pointer?") - sysAssert(ol.typ != nil, "growObj: 1") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") - sysAssert(allocInv(gch.region), "growObj begin") var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell))) var elemSize = 1 if ol.typ.kind != tyString: elemSize = ol.typ.base.size + incTypeSize ol.typ, newsize - let oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize + var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), newsize-oldsize) sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") - # This can be wrong for intermediate temps that are nevertheless on the - # heap because of lambda lifting: - #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4") - when logGC: - writeCell("growObj old cell", ol) - writeCell("growObj new cell", res) - gcTrace(ol, csZctFreed) - gcTrace(res, csAllocated) - when reallyDealloc: - sysAssert(allocInv(gch.region), "growObj before dealloc") - if ol.refcount shr rcShift <=% 1: - # free immediately to save space: - if (ol.refcount and ZctFlag) != 0: - var j = gch.zct.len-1 - var d = gch.zct.d - while j >= 0: - if d[j] == ol: - d[j] = res - break - dec(j) - rawDealloc(gch.region, ol) + when false: + # this is wrong since seqs can be shared via 'shallow': + when reallyDealloc: rawDealloc(gch.region, ol) else: - # we split the old refcount in 2 parts. XXX This is still not entirely - # correct if the pointer that receives growObj's result is on the stack. - # A better fix would be to emit the location specific write barrier for - # 'growObj', but this is lots of more work and who knows what new problems - # this would create. - res.refcount = rcIncrement or allocColor() - decRef(ol) - else: - sysAssert(ol.typ != nil, "growObj: 5") - zeroMem(ol, sizeof(Cell)) + zeroMem(ol, sizeof(Cell)) when useCellIds: inc gch.idGenerator res.id = gch.idGenerator + release(gch) result = cellToUsr(res) - sysAssert(allocInv(gch.region), "growObj end") when defined(memProfiler): nimProfile(newsize-oldsize) proc growObj(old: pointer, newsize: int): pointer {.rtl.} = @@ -629,12 +485,13 @@ proc GC_dumpHeap*(file: File) = ## can be translated into "dot" syntax via the "heapdump2dot" tool. gch.pDumpHeapFile = file var spaceIter: ObjectSpaceIter - var d = gch.decStack.d - for i in 0 .. < gch.decStack.len: - if isAllocatedPtr(gch.region, d[i]): - c_fprintf(file, "onstack %p\n", d[i]) - else: - c_fprintf(file, "onstack_invalid %p\n", d[i]) + when false: + var d = gch.decStack.d + for i in 0 .. < gch.decStack.len: + if isAllocatedPtr(gch.region, d[i]): + c_fprintf(file, "onstack %p\n", d[i]) + else: + c_fprintf(file, "onstack_invalid %p\n", d[i]) for i in 0 .. < globalMarkersLen: globalMarkers[i]() while true: let x = allObjectsAsProc(gch.region, addr spaceIter) @@ -657,18 +514,8 @@ proc GC_dumpHeap() = # ---------------- cycle collector ------------------------------------------- -include gc_common - proc freeCyclicCell(gch: var GcHeap, c: PCell) = gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?") - - var d = gch.decStack.d - for i in 0..gch.decStack.len-1: - if d[i] == c: - writeCell("freeing ", c) - GC_dumpHeap() - gcAssert d[i] != c, "wtf man, freeing obviously alive stuff?!!" - prepareDealloc(c) gcTrace(c, csCycFreed) when logGC: writeCell("cycle collector dealloc cell", c) @@ -707,15 +554,6 @@ proc markRoot(gch: var GcHeap, c: PCell) {.inline.} = if c.color == 1-gch.black: c.setColor(rcGrey) add(gch.greyStack, c) - elif c.color == rcGrey: - var isGrey = false - var d = gch.decStack.d - for i in 0..gch.decStack.len-1: - if d[i] == c: - isGrey = true - break - if not isGrey: - gcAssert false, "markRoot: root is already grey?!" proc markIncremental(gch: var GcHeap): bool = var L = addr(gch.greyStack.len) @@ -735,30 +573,14 @@ proc markIncremental(gch: var GcHeap): bool = c.setColor(gch.black) forAllChildren(c, waMarkGrey) elif c.color == (1-gch.black): - gcAssert false, "wtf why are there white object in the greystack?" + gcAssert false, "wtf why are there white objects in the greystack?" checkTime() gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty " - - # assert that all local roots are black by now: - var d = gch.decStack.d - var errors = false - for i in 0..gch.decStack.len-1: - gcAssert(isAllocatedPtr(gch.region, d[i]), "markIncremental: isAllocatedPtr 2") - if d[i].color != gch.black: - writeCell("not black ", d[i]) - errors = true - gcAssert(not errors, "wtf something wrong hre") result = true proc markGlobals(gch: var GcHeap) = for i in 0 .. < globalMarkersLen: globalMarkers[i]() -proc markLocals(gch: var GcHeap) = - var d = gch.decStack.d - for i in 0 .. < gch.decStack.len: - sysAssert isAllocatedPtr(gch.region, d[i]), "markLocals" - markRoot(gch, d[i]) - proc doOperation(p: pointer, op: WalkOp) = if p == nil: return var c: PCell = usrToCell(p) @@ -770,11 +592,7 @@ proc doOperation(p: pointer, op: WalkOp) = #if not isAllocatedPtr(gch.region, c): # c_fprintf(stdout, "[GC] decref bug: %p", c) gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef") - gcAssert(c.refcount >=% rcIncrement, "doOperation 2") - #c.refcount = c.refcount -% rcIncrement - when logGC: writeCell("decref (from doOperation)", c) - decRef(c) - #if c.refcount <% rcIncrement: addZCT(gch.zct, c) + discard "use me for nimEscape?" of waMarkGlobal: template handleRoot = if gch.dumpHeapFile.isNil: @@ -805,40 +623,6 @@ proc doOperation(p: pointer, op: WalkOp) = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, WalkOp(op)) -proc collectZCT(gch: var GcHeap): bool {.benign.} - -proc collectCycles(gch: var GcHeap): bool = - when hasThreadSupport: - for c in gch.toDispose: - nimGCunref(c) - - # ensure the ZCT 'color' is not used: - while gch.zct.len > 0: discard collectZCT(gch) - - case gch.phase - of Phase.None: - gch.phase = Phase.Marking - markGlobals(gch) - - c_fprintf(stdout, "collectCycles: introduced bug E %ld\n", gch.phase) - discard allocInv(gch.region) - of Phase.Marking: - # since locals do not have a write barrier, we need - # to keep re-scanning them :-( but there is really nothing we can - # do about that. - markLocals(gch) - if markIncremental(gch): - gch.phase = Phase.Sweeping - gch.red = 1 - gch.red - of Phase.Sweeping: - gcAssert gch.greyStack.len == 0, "greystack not empty" - if sweep(gch): - gch.phase = Phase.None - # flip black/white meanings: - gch.black = 1 - gch.black - gcAssert gch.red == 1 - gch.black, "red color is wrong" - result = true - proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") @@ -849,60 +633,43 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) if objStart != nil: # mark the cell: - objStart.refcount = objStart.refcount +% rcIncrement - add(gch.decStack, objStart) + markRoot(gch, objStart) sysAssert(allocInv(gch.region), "gcMark end") proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} = forEachStackSlot(gch, gcMark) -proc collectZCT(gch: var GcHeap): bool = - # Note: Freeing may add child objects to the ZCT! So essentially we do - # deep freeing, which is bad for incremental operation. In order to - # avoid a deep stack, we move objects to keep the ZCT small. - # This is performance critical! - var L = addr(gch.zct.len) - takeStartTime(100) - - while L[] > 0: - var c = gch.zct.d[0] - sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr") - # remove from ZCT: - gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT") - - c.refcount = c.refcount and not ZctFlag - gch.zct.d[0] = gch.zct.d[L[] - 1] - dec(L[]) - takeTime() - if c.refcount <% rcIncrement and c.color != rcGrey: - # It may have a RC > 0, if it is in the hardware stack or - # it has not been removed yet from the ZCT. This is because - # ``incref`` does not bother to remove the cell from the ZCT - # as this might be too slow. - # In any case, it should be removed from the ZCT. But not - # freed. **KEEP THIS IN MIND WHEN MAKING THIS INCREMENTAL!** - when logGC: writeCell("zct dealloc cell", c) - gcTrace(c, csZctFreed) - # We are about to free the object, call the finalizer BEFORE its - # children are deleted as well, because otherwise the finalizer may - # access invalid memory. This is done by prepareDealloc(): - prepareDealloc(c) - forAllChildren(c, waZctDecRef) - when reallyDealloc: - sysAssert(allocInv(gch.region), "collectZCT: rawDealloc") - rawDealloc(gch.region, c) - else: - sysAssert(c.typ != nil, "collectZCT 2") - zeroMem(c, sizeof(Cell)) - checkTime() - result = true - -proc unmarkStackAndRegisters(gch: var GcHeap) = - var d = gch.decStack.d - for i in 0..gch.decStack.len-1: - sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters" - decRef(d[i]) - gch.decStack.len = 0 +proc collectALittle(gch: var GcHeap): bool = + case gch.phase + of Phase.None: + if getOccupiedMem(gch.region) >= gch.cycleThreshold: + gch.phase = Phase.Marking + markGlobals(gch) + result = collectALittle(gch) + #when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase) + #discard allocInv(gch.region) + of Phase.Marking: + when hasThreadSupport: + for c in gch.toDispose: + nimGCunref(c) + prepareForInteriorPointerChecking(gch.region) + markStackAndRegisters(gch) + inc(gch.stat.stackScans) + if markIncremental(gch): + gch.phase = Phase.Sweeping + gch.red = 1 - gch.red + of Phase.Sweeping: + gcAssert gch.greyStack.len == 0, "greystack not empty" + when hasThreadSupport: + for c in gch.toDispose: + nimGCunref(c) + if sweep(gch): + gch.phase = Phase.None + # flip black/white meanings: + gch.black = 1 - gch.black + gcAssert gch.red == 1 - gch.black, "red color is wrong" + inc(gch.stat.completedCollections) + result = true proc collectCTBody(gch: var GcHeap) = when withRealTime: @@ -911,22 +678,12 @@ proc collectCTBody(gch: var GcHeap) = when not nimCoroutines: gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize()) - sysAssert(gch.decStack.len == 0, "collectCT") - prepareForInteriorPointerChecking(gch.region) - markStackAndRegisters(gch) - gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len) - inc(gch.stat.stackScans) - if collectZCT(gch): - when cycleGC: - if getOccupiedMem(gch.region) >= gch.cycleThreshold or alwaysCycleGC: - if collectCycles(gch): - inc(gch.stat.cycleCollections) - gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() * - CycleIncrease) - gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) - unmarkStackAndRegisters(gch) + #gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len) + if collectALittle(gch): + gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() * + CycleIncrease) + gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) sysAssert(allocInv(gch.region), "collectCT: end") - when withRealTime: let duration = getticks() - t0 gch.stat.maxPause = max(gch.stat.maxPause, duration) @@ -947,7 +704,7 @@ proc collectCT(gch: var GcHeap) = let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold) else: let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold) - if (gch.zct.len >= stackMarkCosts or (cycleGC and + if (gch.greyStack.len >= stackMarkCosts or (cycleGC and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and gch.recGcLock == 0: collectCTBody(gch) @@ -961,10 +718,9 @@ when withRealTime: proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) = gch.maxPause = us.toNano - if (gch.zct.len >= ZctThreshold or (cycleGC and - getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) or - strongAdvice: - collectCTBody(gch) + #if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or + # alwaysGC or strongAdvice: + collectCTBody(gch) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} = if stackSize >= 0: @@ -1002,12 +758,8 @@ when not defined(useNimRtl): proc GC_setStrategy(strategy: GC_Strategy) = discard - proc GC_enableMarkAndSweep() = - gch.cycleThreshold = InitialCycleThreshold - - proc GC_disableMarkAndSweep() = - gch.cycleThreshold = high(gch.cycleThreshold)-1 - # set to the max value to suppress the cycle detector + proc GC_enableMarkAndSweep() = discard + proc GC_disableMarkAndSweep() = discard proc GC_fullCollect() = var oldThreshold = gch.cycleThreshold @@ -1021,17 +773,17 @@ when not defined(useNimRtl): "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" & "[GC] stack scans: " & $gch.stat.stackScans & "\n" & "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" & - "[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" & + "[GC] completed collections: " & $gch.stat.completedCollections & "\n" & "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" & - "[GC] zct capacity: " & $gch.zct.cap & "\n" & + "[GC] grey stack capacity: " & $gch.greyStack.cap & "\n" & "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" & - "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) + "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n" when nimCoroutines: - result = result & "[GC] number of stacks: " & $gch.stack.len & "\n" + result.add "[GC] number of stacks: " & $gch.stack.len & "\n" for stack in items(gch.stack): - result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n" + result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n" else: - result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" + result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" GC_enable() {.pop.} From f151b7efd169c740e8a971c8b39eeb71ec9007e4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 28 Sep 2017 21:52:23 +0200 Subject: [PATCH 42/56] added missing test for methods --- tests/method/tnildispatcher.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/method/tnildispatcher.nim diff --git a/tests/method/tnildispatcher.nim b/tests/method/tnildispatcher.nim new file mode 100644 index 0000000000..017e8155f1 --- /dev/null +++ b/tests/method/tnildispatcher.nim @@ -0,0 +1,21 @@ +discard """ + outputsub: '''Error: unhandled exception: cannot dispatch; dispatcher is nil [NilAccessError]''' + exitcode: 1 +""" +# bug #5599 +type + Base = ref object of RootObj + Derived = ref object of Base + +method inner(obj: Base) {.base.} = + quit "to override" + +method outer(obj: Base) {.base.} = + echo "outer" + obj.inner() + +method inner(obj: Derived) = + echo "inner Derived" + +var x: Derived +x.outer() From 70e94b8c9e5d68b19784675bebb12b255a4c83cf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 29 Sep 2017 11:20:28 +0200 Subject: [PATCH 43/56] GCs: use add instead of & --- lib/system/gc.nim | 6 +++--- lib/system/gc_ms.nim | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 13778ee5eb..a2ff72a306 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -945,10 +945,10 @@ when not defined(useNimRtl): "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" & "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n" when nimCoroutines: - result = result & "[GC] number of stacks: " & $gch.stack.len & "\n" + result.add "[GC] number of stacks: " & $gch.stack.len & "\n" for stack in items(gch.stack): - result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n" + result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & cast[pointer](stack.maxStackSize).repr & "\n" else: - result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" + result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" {.pop.} # profiler: off, stackTrace: off diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 494c32b1a7..cfc0dfa8af 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -526,10 +526,10 @@ when not defined(useNimRtl): "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" & "[GC] freed objects: " & $gch.stat.freedObjects & "\n" when nimCoroutines: - result = result & "[GC] number of stacks: " & $gch.stack.len & "\n" + result.add "[GC] number of stacks: " & $gch.stack.len & "\n" for stack in items(gch.stack): - result = result & "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n" + result.add "[GC] stack " & stack.bottom.repr & "[GC] max stack size " & $stack.maxStackSize & "\n" else: - result = result & "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" + result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n" {.pop.} From 0314075639465e3d85693a443e128c02e45e1b53 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 29 Sep 2017 11:33:40 +0200 Subject: [PATCH 44/56] revert the 'wasMoved' logic until the write barrier has been adapted --- lib/system/sysstr.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index a8d91b39cf..90201202c1 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -95,7 +95,8 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} = if str == nil: NimString(nil) else: toNimStr(str, str.len) -template wasMoved(x: NimString): bool = (x.reserved and seqShallowFlag) != 0 +template wasMoved(x: NimString): bool = false +# (x.reserved and seqShallowFlag) != 0 proc copyString(src: NimString): NimString {.compilerRtl.} = if src != nil: From 4b350d2632435883ecb24230ba0eb7e710be0ba7 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 30 Sep 2017 15:36:13 +0200 Subject: [PATCH 45/56] osproc: fixes a memory leak affecting Windows --- lib/pure/osproc.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index fa723f593f..07429b9a90 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -410,13 +410,11 @@ when defined(Windows) and not defined(useNimRtl): result.readDataImpl = hsReadData result.writeDataImpl = hsWriteData - proc buildCommandLine(a: string, args: openArray[string]): cstring = - var res = quoteShell(a) + proc buildCommandLine(a: string, args: openArray[string]): string = + result = quoteShell(a) for i in 0..high(args): - res.add(' ') - res.add(quoteShell(args[i])) - result = cast[cstring](alloc0(res.len+1)) - copyMem(result, cstring(res), res.len) + result.add(' ') + result.add(quoteShell(args[i])) proc buildEnv(env: StringTableRef): tuple[str: cstring, len: int] = var L = 0 @@ -540,11 +538,13 @@ when defined(Windows) and not defined(useNimRtl): result.errHandle = FileHandle(si.hStdError) var cmdl: cstring + var cmdRoot: string if poEvalCommand in options: cmdl = command assert args.len == 0 else: - cmdl = buildCommandLine(command, args) + cmdRoot = buildCommandLine(command, args) + cmdl = cstring(cmdRoot) var wd: cstring = nil var e = (str: nil.cstring, len: -1) if len(workingDir) > 0: wd = workingDir From 7b63ee85b93caff1a992171cf4ae5a04c330d726 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 30 Sep 2017 15:37:01 +0200 Subject: [PATCH 46/56] fixes #6452 --- compiler/extccomp.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 49fdd9ea50..4614eafe6d 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -652,9 +652,10 @@ proc getLinkCmd(projectfile, objfiles: string): string = else: var linkerExe = getConfigVar(cCompiler, ".linkerexe") if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe + # bug #6452: We must not use ``quoteShell`` here for ``linkerExe`` if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") - if noAbsolutePaths(): result = quoteShell(linkerExe) - else: result = quoteShell(joinPath(ccompilerpath, linkerExe)) + if noAbsolutePaths(): result = linkerExe + else: result = joinPath(ccompilerpath, linkerExe) let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui else: "" var exefile, builddll: string From 7e07fc5893baf40093a44c1cee71feea88555f71 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 30 Sep 2017 15:37:36 +0200 Subject: [PATCH 47/56] finish.nim tool: make path environment creation more robust --- tools/finish.nim | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tools/finish.nim b/tools/finish.nim index a6f689eac0..45d7dd3a87 100644 --- a/tools/finish.nim +++ b/tools/finish.nim @@ -91,16 +91,25 @@ when defined(windows): except IOError: echo "Could not access 'config/nim.cfg' [Error]" - proc addToPathEnv*(e: string) = - var p: string + proc tryGetUnicodeValue(path, key: string; handle: HKEY): string = try: - p = getUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER) - except OSError: - p = getUnicodeValue( + result = getUnicodeValue(path, key, handle) + except: + result = "" + + proc addToPathEnv*(e: string) = + var p = tryGetUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER) + if p.len == 0: + p = tryGetUnicodeValue( r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", "Path", HKEY_LOCAL_MACHINE) let x = if e.contains(Whitespace): "\"" & e & "\"" else: e - setUnicodeValue(r"Environment", "Path", p & ";" & x, HKEY_CURRENT_USER) + if p.len > 0: + p.add ";" + p.add x + else: + p = x + setUnicodeValue(r"Environment", "Path", p, HKEY_CURRENT_USER) proc createShortcut(src, dest: string; icon = "") = var cmd = "bin\\makelink.exe \"" & src & "\" \"\" \"" & dest & From 5cf789ac3fffeb895af3c43c0348c537af00c12e Mon Sep 17 00:00:00 2001 From: GULPF Date: Sat, 30 Sep 2017 15:39:00 +0200 Subject: [PATCH 48/56] Improved unittest check macro, fixes #5784 (#6446) --- lib/pure/unittest.nim | 85 +++++++++++++++++++------------------- tests/stdlib/tunittest.nim | 27 +++++++++--- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 28691fcb43..3772a213ac 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -509,10 +509,6 @@ macro check*(conditions: untyped): untyped = ## "AKB48".toLowerAscii() == "akb48" ## 'C' in teams let checked = callsite()[1] - var - argsAsgns = newNimNode(nnkStmtList) - argsPrintOuts = newNimNode(nnkStmtList) - counter = 0 template asgn(a: untyped, value: typed) = var a = value # XXX: we need "var: var" here in order to @@ -522,66 +518,71 @@ macro check*(conditions: untyped): untyped = when compiles(string($value)): checkpoint(name & " was " & $value) - proc inspectArgs(exp: NimNode): NimNode = - result = copyNimTree(exp) + proc inspectArgs(exp: NimNode): tuple[assigns, check, printOuts: NimNode] = + result.check = copyNimTree(exp) + result.assigns = newNimNode(nnkStmtList) + result.printOuts = newNimNode(nnkStmtList) + + var counter = 0 + if exp[0].kind == nnkIdent and - $exp[0] in ["and", "or", "not", "in", "notin", "==", "<=", + $exp[0] in ["not", "in", "notin", "==", "<=", ">=", "<", ">", "!=", "is", "isnot"]: - for i in countup(1, exp.len - 1): + + for i in 1 ..< exp.len: if exp[i].kind notin nnkLiterals: inc counter - var arg = newIdentNode(":p" & $counter) - var argStr = exp[i].toStrLit - var paramAst = exp[i] + let argStr = exp[i].toStrLit + let paramAst = exp[i] if exp[i].kind == nnkIdent: - argsPrintOuts.add getAst(print(argStr, paramAst)) - if exp[i].kind in nnkCallKinds: - var callVar = newIdentNode(":c" & $counter) - argsAsgns.add getAst(asgn(callVar, paramAst)) - result[i] = callVar - argsPrintOuts.add getAst(print(argStr, callVar)) + result.printOuts.add getAst(print(argStr, paramAst)) + if exp[i].kind in nnkCallKinds + { nnkDotExpr, nnkBracketExpr }: + let callVar = newIdentNode(":c" & $counter) + result.assigns.add getAst(asgn(callVar, paramAst)) + result.check[i] = callVar + result.printOuts.add getAst(print(argStr, callVar)) if exp[i].kind == nnkExprEqExpr: # ExprEqExpr # Ident !"v" # IntLit 2 - result[i] = exp[i][1] + result.check[i] = exp[i][1] if exp[i].typekind notin {ntyTypeDesc}: - argsAsgns.add getAst(asgn(arg, paramAst)) - argsPrintOuts.add getAst(print(argStr, arg)) + let arg = newIdentNode(":p" & $counter) + result.assigns.add getAst(asgn(arg, paramAst)) + result.printOuts.add getAst(print(argStr, arg)) if exp[i].kind != nnkExprEqExpr: - result[i] = arg + result.check[i] = arg else: - result[i][1] = arg + result.check[i][1] = arg case checked.kind of nnkCallKinds: - template rewrite(call, lineInfoLit, callLit, - argAssgs, argPrintOuts) = - block: - argAssgs #all callables (and assignments) are run here - if not call: - checkpoint(lineInfoLit & ": Check failed: " & callLit) - argPrintOuts - fail() - var checkedStr = checked.toStrLit - let parameterizedCheck = inspectArgs(checked) - result = getAst(rewrite(parameterizedCheck, checked.lineinfo, checkedStr, - argsAsgns, argsPrintOuts)) + let (assigns, check, printOuts) = inspectArgs(checked) + let lineinfo = newStrLitNode(checked.lineinfo) + let callLit = checked.toStrLit + result = quote do: + block: + `assigns` + if not `check`: + checkpoint(`lineinfo` & ": Check failed: " & `callLit`) + `printOuts` + fail() of nnkStmtList: result = newNimNode(nnkStmtList) - for i in countup(0, checked.len - 1): - if checked[i].kind != nnkCommentStmt: - result.add(newCall(!"check", checked[i])) + for node in checked: + if node.kind != nnkCommentStmt: + result.add(newCall(!"check", node)) else: - template rewrite(exp, lineInfoLit, expLit) = - if not exp: - checkpoint(lineInfoLit & ": Check failed: " & expLit) - fail() + let lineinfo = newStrLitNode(checked.lineinfo) + let callLit = checked.toStrLit - result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit)) + result = quote do: + if not `checked`: + checkpoint(`lineinfo` & ": Check failed: " & `callLit`) + fail() template require*(conditions: untyped) = ## Same as `check` except any failed test causes the program to quit diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 674ce50ddc..e4a8018713 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -1,12 +1,23 @@ discard """ - nimout: "compile start\ncompile end" + output: '''[Suite] suite with only teardown + +[Suite] suite with only setup + +[Suite] suite with none + +[Suite] suite with both + +[Suite] bug #4494 + +[Suite] bug #5571 + +[Suite] bug #5784 + +''' """ import unittest, sequtils -static: - echo "compile start" - proc doThings(spuds: var int): int = spuds = 24 return 99 @@ -103,5 +114,9 @@ suite "bug #5571": check: line == "a" doTest() -static: - echo "compile end" +suite "bug #5784": + test "`or` should short circuit": + type Obj = ref object + field: int + var obj: Obj + check obj.isNil or obj.field == 0 From 3ccc9c467d84dc8c3412acbea20fc10b5335eaa8 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 30 Sep 2017 18:16:09 +0200 Subject: [PATCH 49/56] 'with' and 'without' are not keywords anymore --- compiler/lexer.nim | 4 ++-- compiler/parser.nim | 12 +++++++++--- compiler/pbraces.nim | 11 ++++++++--- compiler/renderer.nim | 4 ++-- compiler/wordrecg.nim | 4 ++-- doc/keywords.txt | 2 +- lib/packages/docutils/highlite.nim | 4 ++-- 7 files changed, 26 insertions(+), 15 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 09bcb4ce04..45d090b168 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -48,7 +48,7 @@ type tkShl, tkShr, tkStatic, tkTemplate, tkTry, tkTuple, tkType, tkUsing, - tkVar, tkWhen, tkWhile, tkWith, tkWithout, tkXor, + tkVar, tkWhen, tkWhile, tkXor, tkYield, # end of keywords tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit, @@ -89,7 +89,7 @@ const "shl", "shr", "static", "template", "try", "tuple", "type", "using", - "var", "when", "while", "with", "without", "xor", + "var", "when", "while", "xor", "yield", "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit", "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit", diff --git a/compiler/parser.nim b/compiler/parser.nim index 06ecf900e3..924f7f26c8 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1038,9 +1038,15 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, optInd(p, result) if not isOperator(p.tok) and isExprStart(p): addSon(result, primary(p, mode)) - if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}: - let nodeKind = if p.tok.tokType == tkWith: nkWith - else: nkWithout + if kind == nkDistinctTy and p.tok.tokType == tkSymbol: + # XXX document this feature! + var nodeKind: TNodeKind + if p.tok.ident.s == "with": + nodeKind = nkWith + elif p.tok.ident.s == "without": + nodeKind = nkWithout + else: + return result getTok(p) let list = newNodeP(nodeKind, p) result.addSon list diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim index fa4ccc1397..eba6f0b624 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -906,9 +906,14 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, optInd(p, result) if not isOperator(p.tok) and isExprStart(p): addSon(result, primary(p, mode)) - if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}: - let nodeKind = if p.tok.tokType == tkWith: nkWith - else: nkWithout + if kind == nkDistinctTy and p.tok.tokType == tkSymbol: + var nodeKind: TNodeKind + if p.tok.ident.s == "with": + nodeKind = nkWith + elif p.tok.ident.s == "without": + nodeKind = nkWithout + else: + return result getTok(p) let list = newNodeP(nodeKind, p) result.addSon list diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 05329394ac..bbe81fe376 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1079,9 +1079,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) if n.len > 1: if n[1].kind == nkWith: - putWithSpace(g, tkWith, " with") + putWithSpace(g, tkSymbol, " with") else: - putWithSpace(g, tkWithout, " without") + putWithSpace(g, tkSymbol, " without") gcomma(g, n[1]) else: put(g, tkDistinct, "distinct") diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 773ab8ff50..1e89ea1e21 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -30,7 +30,7 @@ type wMacro, wMethod, wMixin, wMod, wNil, wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, - wWhen, wWhile, wWith, wWithout, wXor, wYield, + wWhen, wWhile, wXor, wYield, wColon, wColonColon, wEquals, wDot, wDotDot, wStar, wMinus, @@ -116,7 +116,7 @@ const "out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static", "template", "try", "tuple", "type", "using", "var", - "when", "while", "with", "without", "xor", + "when", "while", "xor", "yield", ":", "::", "=", ".", "..", diff --git a/doc/keywords.txt b/doc/keywords.txt index 405bf19816..f8b444924d 100644 --- a/doc/keywords.txt +++ b/doc/keywords.txt @@ -16,6 +16,6 @@ shl shr static template try tuple type using var -when while with without +when while xor yield diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 06b90768cd..1d396d9e06 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -58,8 +58,8 @@ const "interface", "is", "isnot", "iterator", "let", "macro", "method", "mixin", "mod", "nil", "not", "notin", "object", "of", "or", "out", "proc", "ptr", "raise", "ref", "return", "shl", "shr", "static", - "template", "try", "tuple", "type", "using", "var", "when", "while", "with", - "without", "xor", "yield"] + "template", "try", "tuple", "type", "using", "var", "when", "while", + "xor", "yield"] proc getSourceLanguage*(name: string): SourceLanguage = for i in countup(succ(low(SourceLanguage)), high(SourceLanguage)): From b2c358be96e496c37f4d02b0e886a06ed503af9e Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 30 Sep 2017 18:32:22 +0200 Subject: [PATCH 50/56] the 'using construct is not .experimental anymore --- compiler/semstmts.nim | 2 -- doc/manual/stmts.txt | 4 ---- 2 files changed, 6 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7ad13bebbf..a4dd8f354e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -437,8 +437,6 @@ proc isDiscardUnderscore(v: PSym): bool = proc semUsing(c: PContext; n: PNode): PNode = result = ast.emptyNode if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "using") - if not experimentalMode(c): - localError(n.info, "use the {.experimental.} pragma to enable 'using'") for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if gCmd == cmdIdeTools: suggestStmt(c, a) diff --git a/doc/manual/stmts.txt b/doc/manual/stmts.txt index 5668b8cc2a..3a86a9730e 100644 --- a/doc/manual/stmts.txt +++ b/doc/manual/stmts.txt @@ -547,9 +547,6 @@ Instead of: Using statement --------------- -**Warning**: The ``using`` statement is experimental and has to be -explicitly enabled with the `experimental`:idx: pragma or command line option! - The using statement provides syntactic convenience in modules where the same parameter names and types are used over and over. Instead of: @@ -563,7 +560,6 @@ name ``c`` should default to type ``Context``, ``n`` should default to ``Node`` etc.: .. code-block:: nim - {.experimental.} using c: Context n: Node From ddc131cf07972decc206e033b2dd85a42eb1c98d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 30 Sep 2017 21:35:30 +0200 Subject: [PATCH 51/56] the parser finally parses 'echo {1,2}' as it should --- compiler/parser.nim | 14 +------------- tests/casestmt/tcomputedgoto.nim | 2 +- tests/constr/tconstr1.nim | 4 ++-- tests/constr/tconstr2.nim | 2 +- tests/macros/tdebugstmt.nim | 2 +- tests/magics/tlowhigh.nim | 2 +- tests/misc/mvarious.nim | 2 +- tests/misc/tnew.nim | 2 +- tests/misc/tradix.nim | 10 +++++----- tests/misc/tstrdesc.nim | 4 ++-- tests/misc/tvarnums.nim | 2 +- tests/stdlib/tmath2.nim | 2 +- tests/stdlib/trepr2.nim | 2 +- tests/stdlib/tstrset.nim | 6 +++--- tests/threads/tthreadanalysis.nim | 2 +- tests/threads/tthreadanalysis2.nim | 2 +- tests/varres/tvarres3.nim | 2 +- tests/vm/tarrayboundeval.nim | 2 +- 18 files changed, 26 insertions(+), 38 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 924f7f26c8..e14a8fbdf5 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -255,13 +255,6 @@ proc isUnary(p: TParser): bool = p.tok.strongSpaceB == 0 and p.tok.strongSpaceA > 0: result = true - # versions prior to 0.13.0 used to do this: - when false: - if p.strongSpaces: - result = true - else: - parMessage(p, warnDeprecated, - "will be parsed as unary operator; inconsistent spacing") proc checkBinary(p: TParser) {.inline.} = ## Check if the current parser token is a binary operator. @@ -700,12 +693,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = result = r template somePar() = - if p.tok.strongSpaceA > 0: - if p.strongSpaces: - break - else: - parMessage(p, warnDeprecated, - "a [b] will be parsed as command syntax; spacing") + if p.tok.strongSpaceA > 0: break # progress guaranteed while p.tok.indent < 0 or (p.tok.tokType == tkDot and p.tok.indent >= baseIndent): diff --git a/tests/casestmt/tcomputedgoto.nim b/tests/casestmt/tcomputedgoto.nim index f567174af1..1490729645 100644 --- a/tests/casestmt/tcomputedgoto.nim +++ b/tests/casestmt/tcomputedgoto.nim @@ -18,7 +18,7 @@ type enumA, enumB, enumC, enumD, enumE, enumLast proc vm() = - var instructions: array [0..100, MyEnum] + var instructions: array[0..100, MyEnum] instructions[2] = enumC instructions[3] = enumD instructions[4] = enumA diff --git a/tests/constr/tconstr1.nim b/tests/constr/tconstr1.nim index 28431287c1..b9cf5d00be 100644 --- a/tests/constr/tconstr1.nim +++ b/tests/constr/tconstr1.nim @@ -14,13 +14,13 @@ type proc testSem = var - things: array [0..1, TComplexRecord] = [ + things: array[0..1, TComplexRecord] = [ (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}), (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a', 'b', 'c'})] write(stdout, things[0].x) const - things: array [0..1, TComplexRecord] = [ + things: array[0..1, TComplexRecord] = [ (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}), (s: "hi", x: 69, y: 45, z: 1.0)] #ERROR otherThings = [ # the same diff --git a/tests/constr/tconstr2.nim b/tests/constr/tconstr2.nim index cd00681b88..b16be6c507 100644 --- a/tests/constr/tconstr2.nim +++ b/tests/constr/tconstr2.nim @@ -12,7 +12,7 @@ type chars: set[char]] const - things: array [0..1, TComplexRecord] = [ + things: array[0..1, TComplexRecord] = [ (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}), (s: "hi", x: 69, y: 45, z: 1.0, chars: {})] otherThings = [ # the same diff --git a/tests/macros/tdebugstmt.nim b/tests/macros/tdebugstmt.nim index 740ae7b059..69e6100757 100644 --- a/tests/macros/tdebugstmt.nim +++ b/tests/macros/tdebugstmt.nim @@ -21,7 +21,7 @@ macro debug(n: varargs[untyped]): untyped = add(result, newCall("writeLine", newIdentNode("stdout"), n[i])) var - a: array [0..10, int] + a: array[0..10, int] x = "some string" a[0] = 42 a[1] = 45 diff --git a/tests/magics/tlowhigh.nim b/tests/magics/tlowhigh.nim index 4998b73dc8..60ed13c993 100644 --- a/tests/magics/tlowhigh.nim +++ b/tests/magics/tlowhigh.nim @@ -8,7 +8,7 @@ type myEnum = enum e1, e2, e3, e4, e5 var - a: array [myEnum, int] + a: array[myEnum, int] for i in low(a) .. high(a): a[i] = 0 diff --git a/tests/misc/mvarious.nim b/tests/misc/mvarious.nim index d1587faecf..8efe7c92fd 100644 --- a/tests/misc/mvarious.nim +++ b/tests/misc/mvarious.nim @@ -1,6 +1,6 @@ # Test a submodule #type -# TStringArr = array [0.. *] of string +# TStringArr = array[0.. *] of string proc exportme* = discard diff --git a/tests/misc/tnew.nim b/tests/misc/tnew.nim index 88e8bd02c8..89f34a6213 100644 --- a/tests/misc/tnew.nim +++ b/tests/misc/tnew.nim @@ -9,7 +9,7 @@ type str: string le, ri: PNode - TStressTest = ref array [0..45, array [1..45, TNode]] + TStressTest = ref array[0..45, array[1..45, TNode]] proc finalizer(n: PNode) = write(stdout, n.data) diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim index 36a4f39f6a..07674af188 100644 --- a/tests/misc/tradix.nim +++ b/tests/misc/tradix.nim @@ -13,16 +13,16 @@ type kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode len: int8 - keys: array [0..31, int8] - vals: array [0..31, PRadixNode] + keys: array[0..31, int8] + vals: array[0..31, PRadixNode] TRadixNodeFull = object of TRadixNode - b: array [0..255, PRadixNode] + b: array[0..255, PRadixNode] TRadixNodeLeafBits = object of TRadixNode - b: array [0..7, int] + b: array[0..7, int] TRadixNodeLeafLinear = object of TRadixNode len: int8 - keys: array [0..31, int8] + keys: array[0..31, int8] var root: PRadixNode diff --git a/tests/misc/tstrdesc.nim b/tests/misc/tstrdesc.nim index d231603158..1479fcda83 100644 --- a/tests/misc/tstrdesc.nim +++ b/tests/misc/tstrdesc.nim @@ -1,12 +1,12 @@ var - x: array [0..2, int] + x: array[0..2, int] x = [0, 1, 2] type TStringDesc {.final.} = object len, space: int # len and space without counting the terminating zero - data: array [0..0, char] # for the '\0' character + data: array[0..0, char] # for the '\0' character var emptyString {.exportc: "emptyString".}: TStringDesc diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim index 04a1ef53b4..a5c30c7ebf 100644 --- a/tests/misc/tvarnums.nim +++ b/tests/misc/tvarnums.nim @@ -8,7 +8,7 @@ import strutils type - TBuffer = array [0..10, int8] + TBuffer = array[0..10, int8] proc toVarNum(x: int32, b: var TBuffer) = # encoding: first bit indicates end of number (0 if at end) diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim index 84a49efa92..eb0506f5f3 100644 --- a/tests/stdlib/tmath2.nim +++ b/tests/stdlib/tmath2.nim @@ -64,7 +64,7 @@ proc TestLoops() = var glob: int - a: array [0..5, int] + a: array[0..5, int] proc main() = #glob = 0 diff --git a/tests/stdlib/trepr2.nim b/tests/stdlib/trepr2.nim index a920248515..300df565d7 100644 --- a/tests/stdlib/trepr2.nim +++ b/tests/stdlib/trepr2.nim @@ -6,7 +6,7 @@ type TPoint {.final.} = object x, y, z: int - s: array [0..1, string] + s: array[0..1, string] e: TEnum var diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim index 15024c3f1c..1b253f8625 100644 --- a/tests/stdlib/tstrset.nim +++ b/tests/stdlib/tstrset.nim @@ -7,10 +7,10 @@ type kind: TRadixNodeKind TRadixNodeLinear = object of TRadixNode len: int8 - keys: array [0..31, char] - vals: array [0..31, PRadixNode] + keys: array[0..31, char] + vals: array[0..31, PRadixNode] TRadixNodeFull = object of TRadixNode - b: array [char, PRadixNode] + b: array[char, PRadixNode] TRadixNodeLeaf = object of TRadixNode s: string PRadixNodeLinear = ref TRadixNodeLinear diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim index 5b0b666ac7..8aacc71d1e 100644 --- a/tests/threads/tthreadanalysis.nim +++ b/tests/threads/tthreadanalysis.nim @@ -9,7 +9,7 @@ discard """ import os var - thr: array [0..5, Thread[tuple[a, b: int]]] + thr: array[0..5, Thread[tuple[a, b: int]]] proc doNothing() = discard diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim index 93d169f0bb..c1ec3ae39b 100644 --- a/tests/threads/tthreadanalysis2.nim +++ b/tests/threads/tthreadanalysis2.nim @@ -8,7 +8,7 @@ discard """ import os var - thr: array [0..5, Thread[tuple[a, b: int]]] + thr: array[0..5, Thread[tuple[a, b: int]]] proc doNothing() = discard diff --git a/tests/varres/tvarres3.nim b/tests/varres/tvarres3.nim index 9fcd79bfcd..9a46bfb4e0 100644 --- a/tests/varres/tvarres3.nim +++ b/tests/varres/tvarres3.nim @@ -6,7 +6,7 @@ var g = 5 proc p(): var int = - var bla = addr(g) #: array [0..7, int] + var bla = addr(g) #: array[0..7, int] result = bla[] p() = 45 diff --git a/tests/vm/tarrayboundeval.nim b/tests/vm/tarrayboundeval.nim index af9e33339c..dc8c7ebdc9 100644 --- a/tests/vm/tarrayboundeval.nim +++ b/tests/vm/tarrayboundeval.nim @@ -25,7 +25,7 @@ echo myconst, " ", int((KeyMax + 31) / 32) #bug 1304 or something: -const constArray: array [-3..2, int] = [-3, -2, -1, 0, 1, 2] +const constArray: array[-3..2, int] = [-3, -2, -1, 0, 1, 2] echo constArray[-2] From e8eb496e6fda1e076d59fc01118af411e488bf34 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 30 Sep 2017 22:34:54 +0200 Subject: [PATCH 52/56] make tests green again --- tests/concepts/t5642.nim | 2 +- tests/js/tclosures.nim | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/concepts/t5642.nim b/tests/concepts/t5642.nim index d1e7bd1ddf..f08b4629be 100644 --- a/tests/concepts/t5642.nim +++ b/tests/concepts/t5642.nim @@ -19,7 +19,7 @@ proc nrow*(dt: DataTable) : Natural = return totalLen let - stud = Students (id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32]) + stud = Students(id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32]) echo nrow(stud) diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim index 0ec4f47433..67243c937c 100644 --- a/tests/js/tclosures.nim +++ b/tests/js/tclosures.nim @@ -23,29 +23,29 @@ asm """ """ proc consoleprint (str:cstring): void {.importc: "print", noDecl.} -proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join (a, " ")] +proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")] type CallbackProc {.importc.} = proc () : cstring proc regCallback (fn:CallbackProc) {.importc.} proc runCallbacks ():cstring {.importc.} -proc `*` (s:string, n:Natural) : string = s.repeat (n) +proc `*` (s:string, n:Natural) : string = s.repeat(n) proc outer (i:Natural) : (string, int) = - let c = $char (random (93) + 33) - let n = random (40) + let c = $char(random(93) + 33) + let n = random(40) let s = c * n - proc inner () : cstring = ("[$1]" % $n) & s & " <--" - regCallback (inner) + proc inner(): cstring = ("[$1]" % $n) & s & " <--" + regCallback(inner) return (s, n) var expected = "\n" for i in 1 .. 10: - let (s, n) = outer (i) + let (s, n) = outer(i) expected &= ("($1)[$2]" % [$i, $n]) & s & " <--" expected &= "\n" -let results = runCallbacks () +let results = runCallbacks() doAssert(expected == results) From 68c7da227f799fac6d633315aaeade46ae081291 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 1 Oct 2017 07:46:13 +0200 Subject: [PATCH 53/56] make tests green again --- tests/macros/tvtable.nim | 4 ++-- tests/misc/tmandelbrot.nim | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/macros/tvtable.nim b/tests/macros/tvtable.nim index cc5d7a5d91..dc07a726b7 100644 --- a/tests/macros/tvtable.nim +++ b/tests/macros/tvtable.nim @@ -58,10 +58,10 @@ proc create(T: typedesc): T = result.vtbl = getVTable(T) proc baseFoo(o: var TBase): int = - return cast[fooProc[TBase]](o.vtbl[0]) (o) + return cast[fooProc[TBase]](o.vtbl[0])(o) proc baseBar(o: var TBase) = - cast[barProc[TBase]](o.vtbl[1]) (o) + cast[barProc[TBase]](o.vtbl[1])(o) var a = TUserObject1.create var b = TUserObject2.create diff --git a/tests/misc/tmandelbrot.nim b/tests/misc/tmandelbrot.nim index e9b7a3e5a1..5046283137 100644 --- a/tests/misc/tmandelbrot.nim +++ b/tests/misc/tmandelbrot.nim @@ -20,38 +20,38 @@ proc `*` (a, b: TComplex): TComplex = proc abs2 (a: TComplex): float = return a.re * a.re + a.im * a.im -var size = parseInt (paramStr (1)) +var size = parseInt(paramStr(1)) var bit = 128 var byteAcc = 0 -stdout.writeLine ("P4") -stdout.write ($size) -stdout.write (" ") -stdout.writeLine ($size) +stdout.writeLine("P4") +stdout.write($size) +stdout.write(" ") +stdout.writeLine($size) -var fsize = float (size) +var fsize = float(size) for y in 0 .. size-1: - var fy = 2.0 * float (y) / fsize - 1.0 + var fy = 2.0 * float(y) / fsize - 1.0 for x in 0 .. size-1: var z = (0.0, 0.0) - var c = (float (2*x) / fsize - 1.5, fy) + var c = (float(2*x) / fsize - 1.5, fy) block iter: for i in 0 .. 49: z = z*z + c - if abs2 (z) >= 4.0: + if abs2(z) >= 4.0: break iter byteAcc = byteAcc + bit if bit > 1: bit = bit div 2 else: - stdout.write (chr (byteAcc)) + stdout.write(chr(byteAcc)) bit = 128 byteAcc = 0 if bit != 128: - stdout.write (chr (byteAcc)) + stdout.write(chr(byteAcc)) bit = 128 byteAcc = 0 From a585748f2747bfa9f5e9d5585a74928a9fd13dc5 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 1 Oct 2017 16:09:05 +0100 Subject: [PATCH 54/56] Remove basic2d/basic3d. --- changelog.md | 4 + lib/pure/basic2d.nim | 855 ---------------------------------- lib/pure/basic3d.nim | 1040 ------------------------------------------ 3 files changed, 4 insertions(+), 1895 deletions(-) delete mode 100644 lib/pure/basic2d.nim delete mode 100644 lib/pure/basic3d.nim diff --git a/changelog.md b/changelog.md index 3c00481995..9f6a83a37e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ ## v0.18.0 - dd/mm/yyyy +### Changes affecting backwards compatibility +- Removed basic2d/basic3d out of the stdlib and into Nimble packages. + These packages deprecated however, use the ``glm``, ``arraymancer``, ``neo`` + or another package. diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim deleted file mode 100644 index 31b3814d61..0000000000 --- a/lib/pure/basic2d.nim +++ /dev/null @@ -1,855 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2013 Robert Persson -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import math -import strutils - - -## Basic 2d support with vectors, points, matrices and some basic utilities. -## Vectors are implemented as direction vectors, ie. when transformed with a matrix -## the translation part of matrix is ignored. -## Operators `+` , `-` , `*` , `/` , `+=` , `-=` , `*=` and `/=` are implemented for vectors and scalars. -## -## Quick start example: -## -## .. code-block:: nim -## -## # Create a matrix which first rotates, then scales and at last translates -## -## var m:Matrix2d=rotate(DEG90) & scale(2.0) & move(100.0,200.0) -## -## # Create a 2d point at (100,0) and a vector (5,2) -## -## var pt:Point2d=point2d(100.0,0.0) -## -## var vec:Vector2d=vector2d(5.0,2.0) -## -## -## pt &= m # transforms pt in place -## -## var pt2:Point2d=pt & m #concatenates pt with m and returns a new point -## -## var vec2:Vector2d=vec & m #concatenates vec with m and returns a new vector - - -const - DEG360* = PI * 2.0 - ## 360 degrees in radians. - DEG270* = PI * 1.5 - ## 270 degrees in radians. - DEG180* = PI - ## 180 degrees in radians. - DEG90* = PI / 2.0 - ## 90 degrees in radians. - DEG60* = PI / 3.0 - ## 60 degrees in radians. - DEG45* = PI / 4.0 - ## 45 degrees in radians. - DEG30* = PI / 6.0 - ## 30 degrees in radians. - DEG15* = PI / 12.0 - ## 15 degrees in radians. - RAD2DEGCONST = 180.0 / PI - ## used internally by DegToRad and RadToDeg - -type - Matrix2d* = object - ## Implements a row major 2d matrix, which means - ## transformations are applied the order they are concatenated. - ## The rightmost column of the 3x3 matrix is left out since normally - ## not used for geometric transformations in 2d. - ax*,ay*,bx*,by*,tx*,ty*:float - Point2d* = object - ## Implements a non-homogeneous 2d point stored as - ## an `x` coordinate and an `y` coordinate. - x*,y*:float - Vector2d* = object - ## Implements a 2d **direction vector** stored as - ## an `x` coordinate and an `y` coordinate. Direction vector means, - ## that when transforming a vector with a matrix, the translational - ## part of the matrix is ignored. - x*,y*:float -{.deprecated: [TMatrix2d: Matrix2d, TPoint2d: Point2d, TVector2d: Vector2d].} - - -# Some forward declarations... -proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d {.noInit.} - ## Creates a new matrix. - ## `ax`,`ay` is the local x axis - ## `bx`,`by` is the local y axis - ## `tx`,`ty` is the translation -proc vector2d*(x,y:float):Vector2d {.noInit,inline.} - ## Returns a new vector (`x`,`y`) -proc point2d*(x,y:float):Point2d {.noInit,inline.} - ## Returns a new point (`x`,`y`) - - - -let - IDMATRIX*:Matrix2d=matrix2d(1.0,0.0,0.0,1.0,0.0,0.0) - ## Quick access to an identity matrix - ORIGO*:Point2d=point2d(0.0,0.0) - ## Quick access to point (0,0) - XAXIS*:Vector2d=vector2d(1.0,0.0) - ## Quick access to an 2d x-axis unit vector - YAXIS*:Vector2d=vector2d(0.0,1.0) - ## Quick access to an 2d y-axis unit vector - - -# *************************************** -# Private utils -# *************************************** - -proc rtos(val:float):string= - return formatFloat(val,ffDefault,0) - -proc safeArccos(v:float):float= - ## assumes v is in range 0.0-1.0, but clamps - ## the value to avoid out of domain errors - ## due to rounding issues - return arccos(clamp(v,-1.0,1.0)) - - -template makeBinOpVector(s) = - ## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors - proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y)) - proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b)) - proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y)) - -template makeBinOpAssignVector(s)= - ## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors - proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y) - proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b) - - -# *************************************** -# Matrix2d implementation -# *************************************** - -proc setElements*(t:var Matrix2d,ax,ay,bx,by,tx,ty:float) {.inline.}= - ## Sets arbitrary elements in an existing matrix. - t.ax=ax - t.ay=ay - t.bx=bx - t.by=by - t.tx=tx - t.ty=ty - -proc matrix2d*(ax,ay,bx,by,tx,ty:float):Matrix2d = - result.setElements(ax,ay,bx,by,tx,ty) - -proc `&`*(a,b:Matrix2d):Matrix2d {.noInit.} = #concatenate matrices - ## Concatenates matrices returning a new matrix. - - # | a.AX a.AY 0 | | b.AX b.AY 0 | - # | a.BX a.BY 0 | * | b.BX b.BY 0 | - # | a.TX a.TY 1 | | b.TX b.TY 1 | - result.setElements( - a.ax * b.ax + a.ay * b.bx, - a.ax * b.ay + a.ay * b.by, - a.bx * b.ax + a.by * b.bx, - a.bx * b.ay + a.by * b.by, - a.tx * b.ax + a.ty * b.bx + b.tx, - a.tx * b.ay + a.ty * b.by + b.ty) - - -proc scale*(s:float):Matrix2d {.noInit.} = - ## Returns a new scale matrix. - result.setElements(s,0,0,s,0,0) - -proc scale*(s:float,org:Point2d):Matrix2d {.noInit.} = - ## Returns a new scale matrix using, `org` as scale origin. - result.setElements(s,0,0,s,org.x-s*org.x,org.y-s*org.y) - -proc stretch*(sx,sy:float):Matrix2d {.noInit.} = - ## Returns new a stretch matrix, which is a - ## scale matrix with non uniform scale in x and y. - result.setElements(sx,0,0,sy,0,0) - -proc stretch*(sx,sy:float,org:Point2d):Matrix2d {.noInit.} = - ## Returns a new stretch matrix, which is a - ## scale matrix with non uniform scale in x and y. - ## `org` is used as stretch origin. - result.setElements(sx,0,0,sy,org.x-sx*org.x,org.y-sy*org.y) - -proc move*(dx,dy:float):Matrix2d {.noInit.} = - ## Returns a new translation matrix. - result.setElements(1,0,0,1,dx,dy) - -proc move*(v:Vector2d):Matrix2d {.noInit.} = - ## Returns a new translation matrix from a vector. - result.setElements(1,0,0,1,v.x,v.y) - -proc rotate*(rad:float):Matrix2d {.noInit.} = - ## Returns a new rotation matrix, which - ## represents a rotation by `rad` radians - let - s=sin(rad) - c=cos(rad) - result.setElements(c,s,-s,c,0,0) - -proc rotate*(rad:float,org:Point2d):Matrix2d {.noInit.} = - ## Returns a new rotation matrix, which - ## represents a rotation by `rad` radians around - ## the origin `org` - let - s=sin(rad) - c=cos(rad) - result.setElements(c,s,-s,c,org.x+s*org.y-c*org.x,org.y-c*org.y-s*org.x) - -proc mirror*(v:Vector2d):Matrix2d {.noInit.} = - ## Returns a new mirror matrix, mirroring - ## around the line that passes through origo and - ## has the direction of `v` - let - sqx=v.x*v.x - sqy=v.y*v.y - nd=1.0/(sqx+sqy) #used to normalize invector - xy2=v.x*v.y*2.0*nd - sqd=nd*(sqx-sqy) - - if nd==Inf or nd==NegInf: - return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity - - result.setElements( - sqd,xy2, - xy2,-sqd, - 0.0,0.0) - -proc mirror*(org:Point2d,v:Vector2d):Matrix2d {.noInit.} = - ## Returns a new mirror matrix, mirroring - ## around the line that passes through `org` and - ## has the direction of `v` - let - sqx=v.x*v.x - sqy=v.y*v.y - nd=1.0/(sqx+sqy) #used to normalize invector - xy2=v.x*v.y*2.0*nd - sqd=nd*(sqx-sqy) - - if nd==Inf or nd==NegInf: - return IDMATRIX #mirroring around a zero vector is arbitrary=>just use identity - - result.setElements( - sqd,xy2, - xy2,-sqd, - org.x-org.y*xy2-org.x*sqd,org.y-org.x*xy2+org.y*sqd) - - - -proc skew*(xskew,yskew:float):Matrix2d {.noInit.} = - ## Returns a new skew matrix, which has its - ## x axis rotated `xskew` radians from the local x axis, and - ## y axis rotated `yskew` radians from the local y axis - result.setElements(cos(yskew),sin(yskew),-sin(xskew),cos(xskew),0,0) - - -proc `$`* (t:Matrix2d):string {.noInit.} = - ## Returns a string representation of the matrix - return rtos(t.ax) & "," & rtos(t.ay) & - "," & rtos(t.bx) & "," & rtos(t.by) & - "," & rtos(t.tx) & "," & rtos(t.ty) - -proc isUniform*(t:Matrix2d,tol=1.0e-6):bool= - ## Checks if the transform is uniform, that is - ## perpendicular axes of equal length, which means (for example) - ## it cannot transform a circle into an ellipse. - ## `tol` is used as tolerance for both equal length comparison - ## and perp. comparison. - - #dot product=0 means perpendicular coord. system: - if abs(t.ax*t.bx+t.ay*t.by)<=tol: - #subtract squared lengths of axes to check if uniform scaling: - if abs((t.ax*t.ax+t.ay*t.ay)-(t.bx*t.bx+t.by*t.by))<=tol: - return true - return false - -proc determinant*(t:Matrix2d):float= - ## Computes the determinant of the matrix. - - #NOTE: equivalent with perp.dot product for two 2d vectors - return t.ax*t.by-t.bx*t.ay - -proc isMirroring* (m:Matrix2d):bool= - ## Checks if the `m` is a mirroring matrix, - ## which means it will reverse direction of a curve transformed with it - return m.determinant<0.0 - -proc inverse*(m:Matrix2d):Matrix2d {.noInit.} = - ## Returns a new matrix, which is the inverse of the matrix - ## If the matrix is not invertible (determinant=0), an EDivByZero - ## will be raised. - let d=m.determinant - if d==0.0: - raise newException(DivByZeroError,"Cannot invert a zero determinant matrix") - - result.setElements( - m.by/d,-m.ay/d, - -m.bx/d,m.ax/d, - (m.bx*m.ty-m.by*m.tx)/d, - (m.ay*m.tx-m.ax*m.ty)/d) - -proc equals*(m1:Matrix2d,m2:Matrix2d,tol=1.0e-6):bool= - ## Checks if all elements of `m1`and `m2` is equal within - ## a given tolerance `tol`. - return - abs(m1.ax-m2.ax)<=tol and - abs(m1.ay-m2.ay)<=tol and - abs(m1.bx-m2.bx)<=tol and - abs(m1.by-m2.by)<=tol and - abs(m1.tx-m2.tx)<=tol and - abs(m1.ty-m2.ty)<=tol - -proc `=~`*(m1,m2:Matrix2d):bool= - ## Checks if `m1`and `m2` is approximately equal, using a - ## tolerance of 1e-6. - equals(m1,m2) - -proc isIdentity*(m:Matrix2d,tol=1.0e-6):bool= - ## Checks is a matrix is approximately an identity matrix, - ## using `tol` as tolerance for each element. - return equals(m,IDMATRIX,tol) - -proc apply*(m:Matrix2d,x,y:var float,translate=false)= - ## Applies transformation `m` onto `x`,`y`, optionally - ## using the translation part of the matrix. - if translate: # positional style transform - let newx=x*m.ax+y*m.bx+m.tx - y=x*m.ay+y*m.by+m.ty - x=newx - else: # delta style transform - let newx=x*m.ax+y*m.bx - y=x*m.ay+y*m.by - x=newx - - - -# *************************************** -# Vector2d implementation -# *************************************** -proc vector2d*(x,y:float):Vector2d = #forward decl. - result.x=x - result.y=y - -proc polarVector2d*(ang:float,len:float):Vector2d {.noInit.} = - ## Returns a new vector with angle `ang` and magnitude `len` - result.x=cos(ang)*len - result.y=sin(ang)*len - -proc slopeVector2d*(slope:float,len:float):Vector2d {.noInit.} = - ## Returns a new vector having slope (dy/dx) given by - ## `slope`, and a magnitude of `len` - let ang=arctan(slope) - result.x=cos(ang)*len - result.y=sin(ang)*len - -proc len*(v:Vector2d):float {.inline.}= - ## Returns the length of the vector. - sqrt(v.x*v.x+v.y*v.y) - -proc `len=`*(v:var Vector2d,newlen:float) {.noInit.} = - ## Sets the length of the vector, keeping its angle. - let fac=newlen/v.len - - if newlen==0.0: - v.x=0.0 - v.y=0.0 - return - - if fac==Inf or fac==NegInf: - #to short for float accuracy - #do as good as possible: - v.x=newlen - v.y=0.0 - else: - v.x*=fac - v.y*=fac - -proc sqrLen*(v:Vector2d):float {.inline.}= - ## Computes the squared length of the vector, which is - ## faster than computing the absolute length. - v.x*v.x+v.y*v.y - -proc angle*(v:Vector2d):float= - ## Returns the angle of the vector. - ## (The counter clockwise plane angle between posetive x axis and `v`) - result=arctan2(v.y,v.x) - if result<0.0: result+=DEG360 - -proc `$` *(v:Vector2d):string= - ## String representation of `v` - result=rtos(v.x) - result.add(",") - result.add(rtos(v.y)) - - -proc `&` *(v:Vector2d,m:Matrix2d):Vector2d {.noInit.} = - ## Concatenate vector `v` with a transformation matrix. - ## Transforming a vector ignores the translational part - ## of the matrix. - - # | AX AY 0 | - # | X Y 1 | * | BX BY 0 | - # | 0 0 1 | - result.x=v.x*m.ax+v.y*m.bx - result.y=v.x*m.ay+v.y*m.by - - -proc `&=`*(v:var Vector2d,m:Matrix2d) {.inline.}= - ## Applies transformation `m` onto `v` in place. - ## Transforming a vector ignores the translational part - ## of the matrix. - - # | AX AY 0 | - # | X Y 1 | * | BX BY 0 | - # | 0 0 1 | - let newx=v.x*m.ax+v.y*m.bx - v.y=v.x*m.ay+v.y*m.by - v.x=newx - - -proc tryNormalize*(v:var Vector2d):bool= - ## Modifies `v` to have a length of 1.0, keeping its angle. - ## If `v` has zero length (and thus no angle), it is left unmodified and - ## false is returned, otherwise true is returned. - - let mag=v.len - - if mag==0.0: - return false - - v.x/=mag - v.y/=mag - return true - - -proc normalize*(v:var Vector2d) {.inline.}= - ## Modifies `v` to have a length of 1.0, keeping its angle. - ## If `v` has zero length, an EDivByZero will be raised. - if not tryNormalize(v): - raise newException(DivByZeroError,"Cannot normalize zero length vector") - -proc transformNorm*(v:var Vector2d,t:Matrix2d)= - ## Applies a normal direction transformation `t` onto `v` in place. - ## The resulting vector is *not* normalized. Transforming a vector ignores the - ## translational part of the matrix. If the matrix is not invertible - ## (determinant=0), an EDivByZero will be raised. - - # transforming a normal is done by transforming - # by the transpose of the inverse of the original matrix - # this can be heavily optimized by precompute and inline - # | | AX AY 0 | ^-1| ^T - # | X Y 1 | * | | BX BY 0 | | - # | | 0 0 1 | | - let d=t.determinant - if(d==0.0): - raise newException(DivByZeroError,"Matrix is not invertible") - let newx = (t.by*v.x-t.ay*v.y)/d - v.y = (t.ax*v.y-t.bx*v.x)/d - v.x = newx - -proc transformInv*(v:var Vector2d,t:Matrix2d)= - ## Applies inverse of a transformation `t` to `v` in place. - ## This is faster than creating an inverse matrix and apply() it. - ## Transforming a vector ignores the translational part - ## of the matrix. If the matrix is not invertible (determinant=0), an EDivByZero - ## will be raised. - let d=t.determinant - - if(d==0.0): - raise newException(DivByZeroError,"Matrix is not invertible") - - let newx=(t.by*v.x-t.bx*v.y)/d - v.y = (t.ax*v.y-t.ay*v.x)/d - v.x = newx - -proc transformNormInv*(v:var Vector2d,t:Matrix2d)= - ## Applies an inverse normal direction transformation `t` onto `v` in place. - ## This is faster than creating an inverse - ## matrix and transformNorm(...) it. Transforming a vector ignores the - ## translational part of the matrix. - - # normal inverse transform is done by transforming - # by the inverse of the transpose of the inverse of the org. matrix - # which is equivalent with transforming with the transpose. - # | | | AX AY 0 |^-1|^T|^-1 | AX BX 0 | - # | X Y 1 | * | | | BX BY 0 | | | = | X Y 1 | * | AY BY 0 | - # | | | 0 0 1 | | | | 0 0 1 | - # This can be heavily reduced to: - let newx=t.ay*v.y+t.ax*v.x - v.y=t.by*v.y+t.bx*v.x - v.x=newx - -proc rotate90*(v:var Vector2d) {.inline.}= - ## Quickly rotates vector `v` 90 degrees counter clockwise, - ## without using any trigonometrics. - swap(v.x,v.y) - v.x= -v.x - -proc rotate180*(v:var Vector2d){.inline.}= - ## Quickly rotates vector `v` 180 degrees counter clockwise, - ## without using any trigonometrics. - v.x= -v.x - v.y= -v.y - -proc rotate270*(v:var Vector2d) {.inline.}= - ## Quickly rotates vector `v` 270 degrees counter clockwise, - ## without using any trigonometrics. - swap(v.x,v.y) - v.y= -v.y - -proc rotate*(v:var Vector2d,rad:float) = - ## Rotates vector `v` `rad` radians in place. - let - s=sin(rad) - c=cos(rad) - newx=c*v.x-s*v.y - v.y=c*v.y+s*v.x - v.x=newx - -proc scale*(v:var Vector2d,fac:float){.inline.}= - ## Scales vector `v` `rad` radians in place. - v.x*=fac - v.y*=fac - -proc stretch*(v:var Vector2d,facx,facy:float){.inline.}= - ## Stretches vector `v` `facx` times horizontally, - ## and `facy` times vertically. - v.x*=facx - v.y*=facy - -proc mirror*(v:var Vector2d,mirrvec:Vector2d)= - ## Mirrors vector `v` using `mirrvec` as mirror direction. - let - sqx=mirrvec.x*mirrvec.x - sqy=mirrvec.y*mirrvec.y - nd=1.0/(sqx+sqy) #used to normalize invector - xy2=mirrvec.x*mirrvec.y*2.0*nd - sqd=nd*(sqx-sqy) - - if nd==Inf or nd==NegInf: - return #mirroring around a zero vector is arbitrary=>keep as is is fastest - - let newx=xy2*v.y+sqd*v.x - v.y=v.x*xy2-sqd*v.y - v.x=newx - - -proc `-` *(v:Vector2d):Vector2d= - ## Negates a vector - result.x= -v.x - result.y= -v.y - -# declare templated binary operators -makeBinOpVector(`+`) -makeBinOpVector(`-`) -makeBinOpVector(`*`) -makeBinOpVector(`/`) -makeBinOpAssignVector(`+=`) -makeBinOpAssignVector(`-=`) -makeBinOpAssignVector(`*=`) -makeBinOpAssignVector(`/=`) - - -proc dot*(v1,v2:Vector2d):float= - ## Computes the dot product of two vectors. - ## Returns 0.0 if the vectors are perpendicular. - return v1.x*v2.x+v1.y*v2.y - -proc cross*(v1,v2:Vector2d):float= - ## Computes the cross product of two vectors, also called - ## the 'perpendicular dot product' in 2d. Returns 0.0 if the vectors - ## are parallel. - return v1.x*v2.y-v1.y*v2.x - -proc equals*(v1,v2:Vector2d,tol=1.0e-6):bool= - ## Checks if two vectors approximately equals with a tolerance. - return abs(v2.x-v1.x)<=tol and abs(v2.y-v1.y)<=tol - -proc `=~` *(v1,v2:Vector2d):bool= - ## Checks if two vectors approximately equals with a - ## hardcoded tolerance 1e-6 - equals(v1,v2) - -proc angleTo*(v1,v2:Vector2d):float= - ## Returns the smallest of the two possible angles - ## between `v1` and `v2` in radians. - var - nv1=v1 - nv2=v2 - if not nv1.tryNormalize or not nv2.tryNormalize: - return 0.0 # zero length vector has zero angle to any other vector - return safeArccos(dot(nv1,nv2)) - -proc angleCCW*(v1,v2:Vector2d):float= - ## Returns the counter clockwise plane angle from `v1` to `v2`, - ## in range 0 - 2*PI - let a=v1.angleTo(v2) - if v1.cross(v2)>=0.0: - return a - return DEG360-a - -proc angleCW*(v1,v2:Vector2d):float= - ## Returns the clockwise plane angle from `v1` to `v2`, - ## in range 0 - 2*PI - let a=v1.angleTo(v2) - if v1.cross(v2)<=0.0: - return a - return DEG360-a - -proc turnAngle*(v1,v2:Vector2d):float= - ## Returns the amount v1 should be rotated (in radians) to equal v2, - ## in range -PI to PI - let a=v1.angleTo(v2) - if v1.cross(v2)<=0.0: - return -a - return a - -proc bisect*(v1,v2:Vector2d):Vector2d {.noInit.}= - ## Computes the bisector between v1 and v2 as a normalized vector. - ## If one of the input vectors has zero length, a normalized version - ## of the other is returned. If both input vectors has zero length, - ## an arbitrary normalized vector is returned. - var - vmag1=v1.len - vmag2=v2.len - - # zero length vector equals arbitrary vector, just change to magnitude to one to - # avoid zero division - if vmag1==0.0: - if vmag2==0: #both are zero length return any normalized vector - return XAXIS - vmag1=1.0 - if vmag2==0.0: vmag2=1.0 - - let - x1=v1.x/vmag1 - y1=v1.y/vmag1 - x2=v2.x/vmag2 - y2=v2.y/vmag2 - - result.x=(x1 + x2) * 0.5 - result.y=(y1 + y2) * 0.5 - - if not result.tryNormalize(): - # This can happen if vectors are colinear. In this special case - # there are actually two bisectors, we select just - # one of them (x1,y1 rotated 90 degrees ccw). - result.x = -y1 - result.y = x1 - - - -# *************************************** -# Point2d implementation -# *************************************** - -proc point2d*(x,y:float):Point2d = - result.x=x - result.y=y - -proc sqrDist*(a,b:Point2d):float= - ## Computes the squared distance between `a` and `b` - let dx=b.x-a.x - let dy=b.y-a.y - result=dx*dx+dy*dy - -proc dist*(a,b:Point2d):float {.inline.}= - ## Computes the absolute distance between `a` and `b` - result=sqrt(sqrDist(a,b)) - -proc angle*(a,b:Point2d):float= - ## Computes the angle of the vector `b`-`a` - let dx=b.x-a.x - let dy=b.y-a.y - result=arctan2(dy,dx) - if result<0: - result += DEG360 - -proc `$` *(p:Point2d):string= - ## String representation of `p` - result=rtos(p.x) - result.add(",") - result.add(rtos(p.y)) - -proc `&`*(p:Point2d,t:Matrix2d):Point2d {.noInit,inline.} = - ## Concatenates a point `p` with a transform `t`, - ## resulting in a new, transformed point. - - # | AX AY 0 | - # | X Y 1 | * | BX BY 0 | - # | TX TY 1 | - result.x=p.x*t.ax+p.y*t.bx+t.tx - result.y=p.x*t.ay+p.y*t.by+t.ty - -proc `&=` *(p:var Point2d,t:Matrix2d) {.inline.}= - ## Applies transformation `t` onto `p` in place. - let newx=p.x*t.ax+p.y*t.bx+t.tx - p.y=p.x*t.ay+p.y*t.by+t.ty - p.x=newx - - -proc transformInv*(p:var Point2d,t:Matrix2d){.inline.}= - ## Applies the inverse of transformation `t` onto `p` in place. - ## If the matrix is not invertable (determinant=0) , EDivByZero will - ## be raised. - - # | AX AY 0 | ^-1 - # | X Y 1 | * | BX BY 0 | - # | TX TY 1 | - let d=t.determinant - if d==0.0: - raise newException(DivByZeroError,"Cannot invert a zero determinant matrix") - let - newx= (t.bx*t.ty-t.by*t.tx+p.x*t.by-p.y*t.bx)/d - p.y = -(t.ax*t.ty-t.ay*t.tx+p.x*t.ay-p.y*t.ax)/d - p.x=newx - - -proc `+`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} = - ## Adds a vector `v` to a point `p`, resulting - ## in a new point. - result.x=p.x+v.x - result.y=p.y+v.y - -proc `+=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} = - ## Adds a vector `v` to a point `p` in place. - p.x+=v.x - p.y+=v.y - -proc `-`*(p:Point2d,v:Vector2d):Point2d {.noInit,inline.} = - ## Subtracts a vector `v` from a point `p`, resulting - ## in a new point. - result.x=p.x-v.x - result.y=p.y-v.y - -proc `-`*(p1,p2:Point2d):Vector2d {.noInit,inline.} = - ## Subtracts `p2`from `p1` resulting in a difference vector. - result.x=p1.x-p2.x - result.y=p1.y-p2.y - -proc `-=`*(p:var Point2d,v:Vector2d) {.noInit,inline.} = - ## Subtracts a vector `v` from a point `p` in place. - p.x-=v.x - p.y-=v.y - -proc equals(p1,p2:Point2d,tol=1.0e-6):bool {.inline.}= - ## Checks if two points approximately equals with a tolerance. - return abs(p2.x-p1.x)<=tol and abs(p2.y-p1.y)<=tol - -proc `=~`*(p1,p2:Point2d):bool {.inline.}= - ## Checks if two vectors approximately equals with a - ## hardcoded tolerance 1e-6 - equals(p1,p2) - -proc polar*(p:Point2d,ang,dist:float):Point2d {.noInit.} = - ## Returns a point with a given angle and distance away from `p` - result.x=p.x+cos(ang)*dist - result.y=p.y+sin(ang)*dist - -proc rotate*(p:var Point2d,rad:float)= - ## Rotates a point in place `rad` radians around origo. - let - c=cos(rad) - s=sin(rad) - newx=p.x*c-p.y*s - p.y=p.y*c+p.x*s - p.x=newx - -proc rotate*(p:var Point2d,rad:float,org:Point2d)= - ## Rotates a point in place `rad` radians using `org` as - ## center of rotation. - let - c=cos(rad) - s=sin(rad) - newx=(p.x - org.x) * c - (p.y - org.y) * s + org.x - p.y=(p.y - org.y) * c + (p.x - org.x) * s + org.y - p.x=newx - -proc scale*(p:var Point2d,fac:float) {.inline.}= - ## Scales a point in place `fac` times with world origo as origin. - p.x*=fac - p.y*=fac - -proc scale*(p:var Point2d,fac:float,org:Point2d){.inline.}= - ## Scales the point in place `fac` times with `org` as origin. - p.x=(p.x - org.x) * fac + org.x - p.y=(p.y - org.y) * fac + org.y - -proc stretch*(p:var Point2d,facx,facy:float){.inline.}= - ## Scales a point in place non uniformly `facx` and `facy` times with - ## world origo as origin. - p.x*=facx - p.y*=facy - -proc stretch*(p:var Point2d,facx,facy:float,org:Point2d){.inline.}= - ## Scales the point in place non uniformly `facx` and `facy` times with - ## `org` as origin. - p.x=(p.x - org.x) * facx + org.x - p.y=(p.y - org.y) * facy + org.y - -proc move*(p:var Point2d,dx,dy:float){.inline.}= - ## Translates a point `dx`, `dy` in place. - p.x+=dx - p.y+=dy - -proc move*(p:var Point2d,v:Vector2d){.inline.}= - ## Translates a point with vector `v` in place. - p.x+=v.x - p.y+=v.y - -proc sgnArea*(a,b,c:Point2d):float= - ## Computes the signed area of the triangle thru points `a`,`b` and `c` - ## result>0.0 for counter clockwise triangle - ## result<0.0 for clockwise triangle - ## This is commonly used to determinate side of a point with respect to a line. - return ((b.x - c.x) * (b.y - a.y)-(b.y - c.y) * (b.x - a.x))*0.5 - -proc area*(a,b,c:Point2d):float= - ## Computes the area of the triangle thru points `a`,`b` and `c` - return abs(sgnArea(a,b,c)) - -proc closestPoint*(p:Point2d,pts:varargs[Point2d]):Point2d= - ## Returns a point selected from `pts`, that has the closest - ## euclidean distance to `p` - assert(pts.len>0) # must have at least one point - - var - bestidx=0 - bestdist=p.sqrDist(pts[0]) - curdist:float - - for idx in 1..high(pts): - curdist=p.sqrDist(pts[idx]) - if curdist=0.0 and ang Date: Sun, 1 Oct 2017 17:17:40 +0100 Subject: [PATCH 55/56] Add tests for examples from Nim in Action. --- .../Chapter3/ChatApp/readme.markdown | 26 ++++ .../Chapter3/ChatApp/src/client.nim | 54 ++++++++ .../Chapter3/ChatApp/src/client.nim.cfg | 1 + .../Chapter3/ChatApp/src/protocol.nim | 55 ++++++++ .../Chapter3/ChatApp/src/server.nim | 84 +++++++++++++ .../Chapter6/WikipediaStats/concurrency.nim | 79 ++++++++++++ .../WikipediaStats/concurrency.nim.cfg | 1 + .../WikipediaStats/concurrency_regex.nim | 64 ++++++++++ .../WikipediaStats/concurrency_regex.nim.cfg | 1 + .../Chapter6/WikipediaStats/naive.nim | 29 +++++ .../WikipediaStats/parallel_counts.nim | 72 +++++++++++ .../WikipediaStats/parallel_counts.nim.cfg | 1 + .../WikipediaStats/race_condition.nim | 13 ++ .../WikipediaStats/race_condition.nim.cfg | 1 + .../WikipediaStats/sequential_counts.nim | 34 +++++ .../WikipediaStats/unguarded_access.nim | 15 +++ .../WikipediaStats/unguarded_access.nim.cfg | 1 + .../Chapter7/Tweeter/Tweeter.nimble | 14 +++ .../Chapter7/Tweeter/public/style.css | 117 ++++++++++++++++++ .../Chapter7/Tweeter/src/createDatabase.nim | 6 + .../Chapter7/Tweeter/src/database.nim | 93 ++++++++++++++ .../Chapter7/Tweeter/src/tweeter.nim | 62 ++++++++++ .../Chapter7/Tweeter/src/views/general.nim | 51 ++++++++ .../Chapter7/Tweeter/src/views/user.nim | 49 ++++++++ .../Chapter7/Tweeter/tests/database_test.nim | 28 +++++ .../Chapter7/Tweeter/tests/database_test.nims | 2 + tests/niminaction/Chapter8/canvas/canvas.nim | 19 +++ .../Chapter8/canvas/canvas_test.nim | 19 +++ tests/niminaction/Chapter8/sdl/sdl.nim | 34 +++++ tests/niminaction/Chapter8/sdl/sdl_test.nim | 25 ++++ tests/niminaction/Chapter8/sfml/sfml.nim | 26 ++++ tests/niminaction/Chapter8/sfml/sfml_test.nim | 9 ++ .../Chapter9/configurator/configurator.nim | 84 +++++++++++++ tests/testament/categories.nim | 44 +++++++ 34 files changed, 1213 insertions(+) create mode 100644 tests/niminaction/Chapter3/ChatApp/readme.markdown create mode 100644 tests/niminaction/Chapter3/ChatApp/src/client.nim create mode 100644 tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg create mode 100644 tests/niminaction/Chapter3/ChatApp/src/protocol.nim create mode 100644 tests/niminaction/Chapter3/ChatApp/src/server.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/concurrency.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg create mode 100644 tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg create mode 100644 tests/niminaction/Chapter6/WikipediaStats/naive.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg create mode 100644 tests/niminaction/Chapter6/WikipediaStats/race_condition.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/race_condition.nim.cfg create mode 100644 tests/niminaction/Chapter6/WikipediaStats/sequential_counts.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim create mode 100644 tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim.cfg create mode 100644 tests/niminaction/Chapter7/Tweeter/Tweeter.nimble create mode 100644 tests/niminaction/Chapter7/Tweeter/public/style.css create mode 100644 tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim create mode 100644 tests/niminaction/Chapter7/Tweeter/src/database.nim create mode 100644 tests/niminaction/Chapter7/Tweeter/src/tweeter.nim create mode 100644 tests/niminaction/Chapter7/Tweeter/src/views/general.nim create mode 100644 tests/niminaction/Chapter7/Tweeter/src/views/user.nim create mode 100644 tests/niminaction/Chapter7/Tweeter/tests/database_test.nim create mode 100644 tests/niminaction/Chapter7/Tweeter/tests/database_test.nims create mode 100644 tests/niminaction/Chapter8/canvas/canvas.nim create mode 100644 tests/niminaction/Chapter8/canvas/canvas_test.nim create mode 100644 tests/niminaction/Chapter8/sdl/sdl.nim create mode 100644 tests/niminaction/Chapter8/sdl/sdl_test.nim create mode 100644 tests/niminaction/Chapter8/sfml/sfml.nim create mode 100644 tests/niminaction/Chapter8/sfml/sfml_test.nim create mode 100644 tests/niminaction/Chapter9/configurator/configurator.nim diff --git a/tests/niminaction/Chapter3/ChatApp/readme.markdown b/tests/niminaction/Chapter3/ChatApp/readme.markdown new file mode 100644 index 0000000000..200b4df1df --- /dev/null +++ b/tests/niminaction/Chapter3/ChatApp/readme.markdown @@ -0,0 +1,26 @@ +# The ChatApp source code + +This directory contains the ChatApp project, which is the project that is +created as part of Chapter 3 of the Nim in Action book. + +To compile run: + +``` +nim c src/client +nim c src/server +``` + +You can then run the ``server`` in one terminal by executing ``./src/server``. + +After doing so you can execute multiple clients in different terminals and have +them communicate via the server. + +To execute a client, make sure to specify the server address and user name +on the command line: + +```bash +./src/client localhost Peter +``` + +You should then be able to start typing in messages and sending them +by pressing the Enter key. \ No newline at end of file diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim b/tests/niminaction/Chapter3/ChatApp/src/client.nim new file mode 100644 index 0000000000..4d139655cf --- /dev/null +++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim @@ -0,0 +1,54 @@ +import os, threadpool, asyncdispatch, asyncnet +import protocol + +proc connect(socket: AsyncSocket, serverAddr: string) {.async.} = + ## Connects the specified AsyncSocket to the specified address. + ## Then receives messages from the server continuously. + echo("Connecting to ", serverAddr) + # Pause the execution of this procedure until the socket connects to + # the specified server. + await socket.connect(serverAddr, 7687.Port) + echo("Connected!") + while true: + # Pause the execution of this procedure until a new message is received + # from the server. + let line = await socket.recvLine() + # Parse the received message using ``parseMessage`` defined in the + # protocol module. + let parsed = parseMessage(line) + # Display the message to the user. + echo(parsed.username, " said ", parsed.message) + +echo("Chat application started") +# Ensure that the correct amount of command line arguments was specified. +if paramCount() < 2: + # Terminate the client early with an error message if there was not + # enough command line arguments specified by the user. + quit("Please specify the server address, e.g. ./client localhost username") + +# Retrieve the first command line argument. +let serverAddr = paramStr(1) +# Retrieve the second command line argument. +let username = paramStr(2) +# Initialise a new asynchronous socket. +var socket = newAsyncSocket() + +# Execute the ``connect`` procedure in the background asynchronously. +asyncCheck connect(socket, serverAddr) +# Execute the ``readInput`` procedure in the background in a new thread. +var messageFlowVar = spawn stdin.readLine() +while true: + # Check if the ``readInput`` procedure returned a new line of input. + if messageFlowVar.isReady(): + # If a new line of input was returned, we can safely retrieve it + # without blocking. + # The ``createMessage`` is then used to create a message based on the + # line of input. The message is then sent in the background asynchronously. + asyncCheck socket.send(createMessage(username, ^messageFlowVar)) + # Execute the ``readInput`` procedure again, in the background in a + # new thread. + messageFlowVar = spawn stdin.readLine() + + # Execute the asyncdispatch event loop, to continue the execution of + # asynchronous procedures. + asyncdispatch.poll() diff --git a/tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg b/tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg new file mode 100644 index 0000000000..aed303eef8 --- /dev/null +++ b/tests/niminaction/Chapter3/ChatApp/src/client.nim.cfg @@ -0,0 +1 @@ +--threads:on diff --git a/tests/niminaction/Chapter3/ChatApp/src/protocol.nim b/tests/niminaction/Chapter3/ChatApp/src/protocol.nim new file mode 100644 index 0000000000..af515861c8 --- /dev/null +++ b/tests/niminaction/Chapter3/ChatApp/src/protocol.nim @@ -0,0 +1,55 @@ +import json + +type + Message* = object + username*: string + message*: string + + MessageParsingError* = object of Exception + +proc parseMessage*(data: string): Message {.raises: [MessageParsingError, KeyError].} = + var dataJson: JsonNode + try: + dataJson = parseJson(data) + except JsonParsingError: + raise newException(MessageParsingError, "Invalid JSON: " & + getCurrentExceptionMsg()) + except: + raise newException(MessageParsingError, "Unknown error: " & + getCurrentExceptionMsg()) + + if not dataJson.hasKey("username"): + raise newException(MessageParsingError, "Username field missing") + + result.username = dataJson["username"].getStr() + if result.username.len == 0: + raise newException(MessageParsingError, "Username field is empty") + + if not dataJson.hasKey("message"): + raise newException(MessageParsingError, "Message field missing") + result.message = dataJson["message"].getStr() + if result.message.len == 0: + raise newException(MessageParsingError, "Message field is empty") + +proc createMessage*(username, message: string): string = + result = $(%{ + "username": %username, + "message": %message + }) & "\c\l" + +when isMainModule: + block: + let data = """{"username": "dom", "message": "hello"}""" + let parsed = parseMessage(data) + doAssert parsed.message == "hello" + doAssert parsed.username == "dom" + + # Test failure + block: + try: + let parsed = parseMessage("asdasd") + except MessageParsingError: + doAssert true + except: + doAssert false + diff --git a/tests/niminaction/Chapter3/ChatApp/src/server.nim b/tests/niminaction/Chapter3/ChatApp/src/server.nim new file mode 100644 index 0000000000..8c572aeb06 --- /dev/null +++ b/tests/niminaction/Chapter3/ChatApp/src/server.nim @@ -0,0 +1,84 @@ +import asyncdispatch, asyncnet + +type + Client = ref object + socket: AsyncSocket + netAddr: string + id: int + connected: bool + + Server = ref object + socket: AsyncSocket + clients: seq[Client] + +proc newServer(): Server = + ## Constructor for creating a new ``Server``. + Server(socket: newAsyncSocket(), clients: @[]) + +proc `$`(client: Client): string = + ## Converts a ``Client``'s information into a string. + $client.id & "(" & client.netAddr & ")" + +proc processMessages(server: Server, client: Client) {.async.} = + ## Loops while ``client`` is connected to this server, and checks + ## whether as message has been received from ``client``. + while true: + # Pause execution of this procedure until a line of data is received from + # ``client``. + let line = await client.socket.recvLine() + + # The ``recvLine`` procedure returns ``""`` (i.e. a string of length 0) + # when ``client`` has disconnected. + if line.len == 0: + echo(client, " disconnected!") + client.connected = false + # When a socket disconnects it must be closed. + client.socket.close() + return + + # Display the message that was sent by the client. + echo(client, " sent: ", line) + + # Send the message to other clients. + for c in server.clients: + # Don't send it to the client that sent this or to a client that is + # disconnected. + if c.id != client.id and c.connected: + await c.socket.send(line & "\c\l") + +proc loop(server: Server, port = 7687) {.async.} = + ## Loops forever and checks for new connections. + + # Bind the port number specified by ``port``. + server.socket.bindAddr(port.Port) + # Ready the server socket for new connections. + server.socket.listen() + echo("Listening on localhost:", port) + + while true: + # Pause execution of this procedure until a new connection is accepted. + let (netAddr, clientSocket) = await server.socket.acceptAddr() + echo("Accepted connection from ", netAddr) + + # Create a new instance of Client. + let client = Client( + socket: clientSocket, + netAddr: netAddr, + id: server.clients.len, + connected: true + ) + # Add this new instance to the server's list of clients. + server.clients.add(client) + # Run the ``processMessages`` procedure asynchronously in the background, + # this procedure will continuously check for new messages from the client. + asyncCheck processMessages(server, client) + +# Check whether this module has been imported as a dependency to another +# module, or whether this module is the main module. +when isMainModule: + # Initialise a new server. + var server = newServer() + echo("Server initialised!") + # Execute the ``loop`` procedure. The ``waitFor`` procedure will run the + # asyncdispatch event loop until the ``loop`` procedure finishes executing. + waitFor loop(server) \ No newline at end of file diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim new file mode 100644 index 0000000000..478f533d9f --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim @@ -0,0 +1,79 @@ +# See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites +import tables, parseutils, strutils, threadpool + +const filename = "pagecounts-20160101-050000" + +type + Stats = ref object + projectName, pageTitle: string + requests, contentSize: int + +proc `$`(stats: Stats): string = + "(projectName: $#, pageTitle: $#, requests: $#, contentSize: $#)" % [ + stats.projectName, stats.pageTitle, $stats.requests, $stats.contentSize + ] + +proc parse(chunk: string): Stats = + # Each line looks like: en Main_Page 242332 4737756101 + result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0) + + var projectName = "" + var pageTitle = "" + var requests = "" + var contentSize = "" + for line in chunk.splitLines: + var i = 0 + projectName.setLen(0) + i.inc parseUntil(line, projectName, Whitespace, i) + i.inc skipWhitespace(line, i) + pageTitle.setLen(0) + i.inc parseUntil(line, pageTitle, Whitespace, i) + i.inc skipWhitespace(line, i) + requests.setLen(0) + i.inc parseUntil(line, requests, Whitespace, i) + i.inc skipWhitespace(line, i) + contentSize.setLen(0) + i.inc parseUntil(line, contentSize, Whitespace, i) + i.inc skipWhitespace(line, i) + + if requests.len == 0 or contentSize.len == 0: + # Ignore lines with either of the params that are empty. + continue + + let requestsInt = requests.parseInt + if requestsInt > result.requests and projectName == "en": + result = Stats( + projectName: projectName, + pageTitle: pageTitle, + requests: requestsInt, + contentSize: contentSize.parseInt + ) + +proc readChunks(filename: string, chunksize = 1000000): Stats = + result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0) + var file = open(filename) + var responses = newSeq[FlowVar[Stats]]() + var buffer = newString(chunksize) + var oldBufferLen = 0 + while not endOfFile(file): + let readSize = file.readChars(buffer, oldBufferLen, chunksize - oldBufferLen) + oldBufferLen + var chunkLen = readSize + + while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines: + # Find where the last line ends + chunkLen.dec + + responses.add(spawn parse(buffer[0 .. result.requests: + result = statistic + + file.close() + + +when isMainModule: + echo readChunks(filename) diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg new file mode 100644 index 0000000000..aed303eef8 --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency.nim.cfg @@ -0,0 +1 @@ +--threads:on diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim new file mode 100644 index 0000000000..8df3b6aeba --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim @@ -0,0 +1,64 @@ +# See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites +import tables, parseutils, strutils, threadpool, re + +const filename = "pagecounts-20160101-050000" + +type + Stats = ref object + projectName, pageTitle: string + requests, contentSize: int + +proc `$`(stats: Stats): string = + "(projectName: $#, pageTitle: $#, requests: $#, contentSize: $#)" % [ + stats.projectName, stats.pageTitle, $stats.requests, $stats.contentSize + ] + +proc parse(chunk: string): Stats = + # Each line looks like: en Main_Page 242332 4737756101 + result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0) + + var matches: array[4, string] + var reg = re"([^\s]+)\s([^\s]+)\s(\d+)\s(\d+)" + for line in chunk.splitLines: + + let start = find(line, reg, matches) + if start == -1: continue + + let requestsInt = matches[2].parseInt + if requestsInt > result.requests and matches[0] == "en": + result = Stats( + projectName: matches[0], + pageTitle: matches[1], + requests: requestsInt, + contentSize: matches[3].parseInt + ) + +proc readChunks(filename: string, chunksize = 1000000): Stats = + result = Stats(projectName: "", pageTitle: "", requests: 0, contentSize: 0) + var file = open(filename) + var responses = newSeq[FlowVar[Stats]]() + var buffer = newString(chunksize) + var oldBufferLen = 0 + while not endOfFile(file): + let readSize = file.readChars(buffer, oldBufferLen, chunksize - oldBufferLen) + oldBufferLen + var chunkLen = readSize + + while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines: + # Find where the last line ends + chunkLen.dec + + responses.add(spawn parse(buffer[0 .. result.requests: + result = statistic + + file.close() + + +when isMainModule: + echo readChunks(filename) diff --git a/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg new file mode 100644 index 0000000000..aed303eef8 --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/concurrency_regex.nim.cfg @@ -0,0 +1 @@ +--threads:on diff --git a/tests/niminaction/Chapter6/WikipediaStats/naive.nim b/tests/niminaction/Chapter6/WikipediaStats/naive.nim new file mode 100644 index 0000000000..ed4fba8e26 --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/naive.nim @@ -0,0 +1,29 @@ +# See this page for info about the format https://wikitech.wikimedia.org/wiki/Analytics/Data/Pagecounts-all-sites +import tables, parseutils, strutils + +const filename = "pagecounts-20150101-050000" + +proc parse(filename: string): tuple[projectName, pageTitle: string, + requests, contentSize: int] = + # Each line looks like: en Main_Page 242332 4737756101 + var file = open(filename) + for line in file.lines: + var i = 0 + var projectName = "" + i.inc parseUntil(line, projectName, Whitespace, i) + i.inc + var pageTitle = "" + i.inc parseUntil(line, pageTitle, Whitespace, i) + i.inc + var requests = 0 + i.inc parseInt(line, requests, i) + i.inc + var contentSize = 0 + i.inc parseInt(line, contentSize, i) + if requests > result[2] and projectName == "en": + result = (projectName, pageTitle, requests, contentSize) + + file.close() + +when isMainModule: + echo parse(filename) diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim new file mode 100644 index 0000000000..7181145e9f --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim @@ -0,0 +1,72 @@ +import os, parseutils, threadpool, strutils + +type + Stats = ref object + domainCode, pageTitle: string + countViews, totalSize: int + +proc newStats(): Stats = + Stats(domainCode: "", pageTitle: "", countViews: 0, totalSize: 0) + +proc `$`(stats: Stats): string = + "(domainCode: $#, pageTitle: $#, countViews: $#, totalSize: $#)" % [ + stats.domainCode, stats.pageTitle, $stats.countViews, $stats.totalSize + ] + +proc parse(line: string, domainCode, pageTitle: var string, + countViews, totalSize: var int) = + if line.len == 0: return + var i = 0 + domainCode.setLen(0) + i.inc parseUntil(line, domainCode, {' '}, i) + i.inc + pageTitle.setLen(0) + i.inc parseUntil(line, pageTitle, {' '}, i) + i.inc + countViews = 0 + i.inc parseInt(line, countViews, i) + i.inc + totalSize = 0 + i.inc parseInt(line, totalSize, i) + +proc parseChunk(chunk: string): Stats = + result = newStats() + var domainCode = "" + var pageTitle = "" + var countViews = 0 + var totalSize = 0 + for line in splitLines(chunk): + parse(line, domainCode, pageTitle, countViews, totalSize) + if domainCode == "en" and countViews > result.countViews: + result = Stats(domainCode: domainCode, pageTitle: pageTitle, + countViews: countViews, totalSize: totalSize) + +proc readPageCounts(filename: string, chunkSize = 1_000_000) = + var file = open(filename) + var responses = newSeq[FlowVar[Stats]]() + var buffer = newString(chunksize) + var oldBufferLen = 0 + while not endOfFile(file): + let reqSize = chunksize - oldBufferLen + let readSize = file.readChars(buffer, oldBufferLen, reqSize) + oldBufferLen + var chunkLen = readSize + + while chunkLen >= 0 and buffer[chunkLen - 1] notin NewLines: + chunkLen.dec + + responses.add(spawn parseChunk(buffer[0 .. mostPopular.countViews: + mostPopular = statistic + + echo("Most popular is: ", mostPopular) + +when isMainModule: + const file = "pagecounts-20160101-050000" + let filename = getCurrentDir() / file + readPageCounts(filename) \ No newline at end of file diff --git a/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg new file mode 100644 index 0000000000..9d57ecf93d --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/parallel_counts.nim.cfg @@ -0,0 +1 @@ +--threads:on \ No newline at end of file diff --git a/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim new file mode 100644 index 0000000000..c62b2f93eb --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/race_condition.nim @@ -0,0 +1,13 @@ +import threadpool + +var counter = 0 + +proc increment(x: int) = + for i in 0 .. mostPopular[2]: + mostPopular = (domainCode, pageTitle, countViews, totalSize) + + echo("Most popular is: ", mostPopular) + +when isMainModule: + const file = "pagecounts-20160101-050000" + let filename = getCurrentDir() / file + readPageCounts(filename) \ No newline at end of file diff --git a/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim new file mode 100644 index 0000000000..72e8bff120 --- /dev/null +++ b/tests/niminaction/Chapter6/WikipediaStats/unguarded_access.nim @@ -0,0 +1,15 @@ +import threadpool, locks + +var counterLock: Lock +initLock(counterLock) +var counter {.guard: counterLock.} = 0 + +proc increment(x: int) = + for i in 0 .. = 0.13.1" +requires "jester >= 0.0.1" diff --git a/tests/niminaction/Chapter7/Tweeter/public/style.css b/tests/niminaction/Chapter7/Tweeter/public/style.css new file mode 100644 index 0000000000..baacfaf9d7 --- /dev/null +++ b/tests/niminaction/Chapter7/Tweeter/public/style.css @@ -0,0 +1,117 @@ +body { + background-color: #f1f9ea; + margin: 0; + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; +} + +div#main { + width: 80%; + margin-left: auto; + margin-right: auto; +} + +div#user { + background-color: #66ac32; + width: 100%; + color: #c7f0aa; + padding: 5pt; +} + +div#user > h1 { + color: #ffffff; +} + +h1 { + margin: 0; + display: inline; + padding-left: 10pt; + padding-right: 10pt; +} + +div#user > form { + float: right; + margin-right: 10pt; +} + +div#user > form > input[type="submit"] { + border: 0px none; + padding: 5pt; + font-size: 108%; + color: #ffffff; + background-color: #515d47; + border-radius: 5px; + cursor: pointer; +} + +div#user > form > input[type="submit"]:hover { + background-color: #538c29; +} + + +div#messages { + background-color: #a2dc78; + width: 90%; + margin-left: auto; + margin-right: auto; + color: #1a1a1a; +} + +div#messages > div { + border-left: 1px solid #869979; + border-right: 1px solid #869979; + border-bottom: 1px solid #869979; + padding: 5pt; +} + +div#messages > div > a, div#messages > div > span { + color: #475340; +} + +div#messages > div > a:hover { + text-decoration: none; + color: #c13746; +} + +h3 { + margin-bottom: 0; + font-weight: normal; +} + +div#login { + width: 200px; + margin-left: auto; + margin-right: auto; + margin-top: 20%; + + font-size: 130%; +} + +div#login span.small { + display: block; + font-size: 56%; +} + +div#newMessage { + background-color: #538c29; + width: 90%; + margin-left: auto; + margin-right: auto; + color: #ffffff; + padding: 5pt; +} + +div#newMessage span { + padding-right: 5pt; +} + +div#newMessage form { + display: inline; +} + +div#newMessage > form > input[type="text"] { + width: 80%; +} + +div#newMessage > form > input[type="submit"] { + font-size: 80%; +} diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim new file mode 100644 index 0000000000..c7aee1b44a --- /dev/null +++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim @@ -0,0 +1,6 @@ +import database + +var db = newDatabase() +db.setup() +echo("Database created successfully!") +db.close() \ No newline at end of file diff --git a/tests/niminaction/Chapter7/Tweeter/src/database.nim b/tests/niminaction/Chapter7/Tweeter/src/database.nim new file mode 100644 index 0000000000..4faba3f6a8 --- /dev/null +++ b/tests/niminaction/Chapter7/Tweeter/src/database.nim @@ -0,0 +1,93 @@ +import times, db_sqlite, strutils #<1> +type #<2> + Database* = ref object + db*: DbConn + + User* = object #<3> + username*: string #<4> + following*: seq[string] #<5> + + Message* = object #<6> + username*: string #<7> + time*: Time #<8> + msg*: string #<9> + +proc newDatabase*(filename = "tweeter.db"): Database = + new result + result.db = open(filename, "", "", "") + +proc close*(database: Database) = + database.db.close() + +proc setup*(database: Database) = + database.db.exec(sql""" + CREATE TABLE IF NOT EXISTS User( + username text PRIMARY KEY + ); + """) + + database.db.exec(sql""" + CREATE TABLE IF NOT EXISTS Following( + follower text, + followed_user text, + PRIMARY KEY (follower, followed_user), + FOREIGN KEY (follower) REFERENCES User(username), + FOREIGN KEY (followed_user) REFERENCES User(username) + ); + """) + + database.db.exec(sql""" + CREATE TABLE IF NOT EXISTS Message( + username text, + time integer, + msg text NOT NULL, + FOREIGN KEY (username) REFERENCES User(username) + ); + """) + +proc post*(database: Database, message: Message) = + if message.msg.len > 140: #<1> + raise newException(ValueError, "Message has to be less than 140 characters.") + + database.db.exec(sql"INSERT INTO Message VALUES (?, ?, ?);", #<2> + message.username, $message.time.toSeconds().int, message.msg) #<3> + +proc follow*(database: Database, follower: User, user: User) = + database.db.exec(sql"INSERT INTO Following VALUES (?, ?);",#<2> + follower.username, user.username) + +proc create*(database: Database, user: User) = + database.db.exec(sql"INSERT INTO User VALUES (?);", user.username) #<2> + +proc findUser*(database: Database, username: string, user: var User): bool = + let row = database.db.getRow( + sql"SELECT username FROM User WHERE username = ?;", username) + if row[0].len == 0: return false + else: user.username = row[0] + + let following = database.db.getAllRows( + sql"SELECT followed_user FROM Following WHERE follower = ?;", username) + user.following = @[] + for row in following: + if row[0].len != 0: + user.following.add(row[0]) + + return true + +proc findMessages*(database: Database, usernames: seq[string], + limit = 10): seq[Message] = + result = @[] + if usernames.len == 0: return + var whereClause = " WHERE " + for i in 0 .. + + + Tweeter written in Nim + + + + + ${body} + + + +#end proc +# +#proc renderLogin*(): string = +# result = "" +
+ Login + Please type in your username... +
+ + +
+
+#end proc +# +#proc renderTimeline*(username: string, messages: seq[Message]): string = +# result = "" +
+

${$!username}'s timeline

+
+
+ New message +
+ + + +
+
+${renderMessages(messages)} +#end proc \ No newline at end of file diff --git a/tests/niminaction/Chapter7/Tweeter/src/views/user.nim b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim new file mode 100644 index 0000000000..f3791b4936 --- /dev/null +++ b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim @@ -0,0 +1,49 @@ +#? stdtmpl(subsChar = '$', metaChar = '#', toString = "xmltree.escape") +#import "../database" +#import xmltree +#import times +# +#proc renderUser*(user: User): string = +# result = "" +
+

${user.username}

+ Following: ${$user.following.len} +
+#end proc +# +#proc renderUser*(user: User, currentUser: User): string = +# result = "" +
+

${user.username}

+ Following: ${$user.following.len} + #if user.username notin currentUser.following: +
+ + + +
+ #end if +
+# +#end proc +# +#proc renderMessages*(messages: seq[Message]): string = +# result = "" +
+ #for message in messages: +
+ ${message.username} + ${message.time.getGMTime().format("HH:mm MMMM d',' yyyy")} +

${message.msg}

+
+ #end for +
+#end proc +# +#when isMainModule: +# echo renderUser(User(username: "d0m96<>", following: @[])) +# echo renderMessages(@[ +# Message(username: "d0m96", time: getTime(), msg: "Hello World!"), +# Message(username: "d0m96", time: getTime(), msg: "Testing") +# ]) +#end when diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim new file mode 100644 index 0000000000..926ca452cb --- /dev/null +++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim @@ -0,0 +1,28 @@ +import database, os, times + +when isMainModule: + removeFile("tweeter_test.db") + var db = newDatabase("tweeter_test.db") + db.setup() + + db.create(User(username: "d0m96")) + db.create(User(username: "nim_lang")) + + db.post(Message(username: "nim_lang", time: getTime() - 4.seconds, + msg: "Hello Nim in Action readers")) + db.post(Message(username: "nim_lang", time: getTime(), + msg: "99.9% off Nim in Action for everyone, for the next minute only!")) + + var dom: User + doAssert db.findUser("d0m96", dom) + var nim: User + doAssert db.findUser("nim_lang", nim) + db.follow(dom, nim) + + doAssert db.findUser("d0m96", dom) + + let messages = db.findMessages(dom.following) + echo(messages) + doAssert(messages[0].msg == "99.9% off Nim in Action for everyone, for the next minute only!") + doAssert(messages[1].msg == "Hello Nim in Action readers") + echo("All tests finished successfully!") diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nims b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nims new file mode 100644 index 0000000000..226905fbf3 --- /dev/null +++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nims @@ -0,0 +1,2 @@ +--path:"../src" +#switch("path", "./src") diff --git a/tests/niminaction/Chapter8/canvas/canvas.nim b/tests/niminaction/Chapter8/canvas/canvas.nim new file mode 100644 index 0000000000..713d1e9e2f --- /dev/null +++ b/tests/niminaction/Chapter8/canvas/canvas.nim @@ -0,0 +1,19 @@ +import dom + +type + CanvasRenderingContext* = ref object + fillStyle* {.importc.}: cstring + strokeStyle* {.importc.}: cstring + +{.push importcpp.} + +proc getContext*(canvasElement: Element, + contextType: cstring): CanvasRenderingContext + +proc fillRect*(context: CanvasRenderingContext, x, y, width, height: int) + +proc moveTo*(context: CanvasRenderingContext, x, y: int) + +proc lineTo*(context: CanvasRenderingContext, x, y: int) + +proc stroke*(context: CanvasRenderingContext) diff --git a/tests/niminaction/Chapter8/canvas/canvas_test.nim b/tests/niminaction/Chapter8/canvas/canvas_test.nim new file mode 100644 index 0000000000..42d222b7b7 --- /dev/null +++ b/tests/niminaction/Chapter8/canvas/canvas_test.nim @@ -0,0 +1,19 @@ +import canvas, dom + +proc onLoad() {.exportc.} = + var canvas = document.getElementById("canvas").EmbedElement + canvas.width = window.innerWidth + canvas.height = window.innerHeight + var ctx = canvas.getContext("2d") + + ctx.fillStyle = "#1d4099" + ctx.fillRect(0, 0, window.innerWidth, window.innerHeight) + + ctx.strokeStyle = "#ffffff" + let letterWidth = 100 + let letterLeftPos = (window.innerWidth div 2) - (letterWidth div 2) + ctx.moveTo(letterLeftPos, 320) + ctx.lineTo(letterLeftPos, 110) + ctx.lineTo(letterLeftPos + letterWidth, 320) + ctx.lineTo(letterLeftPos + letterWidth, 110) + ctx.stroke() diff --git a/tests/niminaction/Chapter8/sdl/sdl.nim b/tests/niminaction/Chapter8/sdl/sdl.nim new file mode 100644 index 0000000000..a1b30281b5 --- /dev/null +++ b/tests/niminaction/Chapter8/sdl/sdl.nim @@ -0,0 +1,34 @@ +when defined(Windows): + const libName* = "SDL2.dll" +elif defined(Linux): + const libName* = "libSDL2.so" +elif defined(MacOsX): + const libName* = "libSDL2.dylib" + +type + SdlWindow = object + SdlWindowPtr* = ptr SdlWindow + SdlRenderer = object + SdlRendererPtr* = ptr SdlRenderer + +const INIT_VIDEO* = 0x00000020 + +{.push dynlib: libName.} +proc init*(flags: uint32): cint {.importc: "SDL_Init".} + +proc createWindowAndRenderer*(width, height: cint, window_flags: cuint, + window: var SdlWindowPtr, renderer: var SdlRendererPtr): cint + {.importc: "SDL_CreateWindowAndRenderer".} + +proc pollEvent*(event: pointer): cint {.importc: "SDL_PollEvent".} + +proc setDrawColor*(renderer: SdlRendererPtr, r, g, b, a: uint8): cint + {.importc: "SDL_SetRenderDrawColor", discardable.} + +proc present*(renderer: SdlRendererPtr) {.importc: "SDL_RenderPresent".} + +proc clear*(renderer: SdlRendererPtr) {.importc: "SDL_RenderClear".} + +proc drawLines*(renderer: SdlRendererPtr, points: ptr tuple[x, y: cint], + count: cint): cint {.importc: "SDL_RenderDrawLines", discardable.} +{.pop.} diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim new file mode 100644 index 0000000000..a572d52316 --- /dev/null +++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim @@ -0,0 +1,25 @@ +import os +import sdl + +if sdl.init(INIT_VIDEO) == -1: + quit("Couldn't initialise SDL") + +var window: SdlWindowPtr +var renderer: SdlRendererPtr +if createWindowAndRenderer(640, 480, 0, window, renderer) == -1: + quit("Couldn't create a window or renderer") + +discard pollEvent(nil) +renderer.setDrawColor 29, 64, 153, 255 +renderer.clear +renderer.setDrawColor 255, 255, 255, 255 +var points = [ + (260'i32, 320'i32), + (260'i32, 110'i32), + (360'i32, 320'i32), + (360'i32, 110'i32) +] +renderer.drawLines(addr points[0], points.len.cint) + +renderer.present +sleep(5000) diff --git a/tests/niminaction/Chapter8/sfml/sfml.nim b/tests/niminaction/Chapter8/sfml/sfml.nim new file mode 100644 index 0000000000..fea85fcd4f --- /dev/null +++ b/tests/niminaction/Chapter8/sfml/sfml.nim @@ -0,0 +1,26 @@ +{.passL: "-lsfml-graphics -lsfml-system -lsfml-window".} + +type + VideoMode* {.importcpp: "sf::VideoMode".} = object + RenderWindowObj {.importcpp: "sf::RenderWindow".} = object + RenderWindow* = ptr RenderWindowObj + Color* {.importcpp: "sf::Color".} = object + Event* {.importcpp: "sf::Event".} = object + +{.push cdecl, header: "".} + +proc videoMode*(modeWidth, modeHeight: cuint, modeBitsPerPixel: cuint = 32): VideoMode + {.importcpp: "sf::VideoMode(@)", constructor.} + +proc newRenderWindow*(mode: VideoMode, title: cstring): RenderWindow + {.importcpp: "new sf::RenderWindow(@)", constructor.} + +proc pollEvent*(window: RenderWindow, event: var Event): bool + {.importcpp: "#.pollEvent(@)".} + +proc newColor*(red, green, blue, alpha: uint8): Color + {.importcpp: "sf::Color(@)", constructor.} + +proc clear*(window: RenderWindow, color: Color) {.importcpp: "#.clear(@)".} + +proc display*(window: RenderWindow) {.importcpp: "#.display()".} diff --git a/tests/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim new file mode 100644 index 0000000000..49a8176e5e --- /dev/null +++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim @@ -0,0 +1,9 @@ +import sfml, os +var window = newRenderWindow(videoMode(800, 600), "SFML works!") + +var event: Event +discard window.pollEvent(event) +window.clear(newColor(29, 64, 153, 255)) +window.display() + +sleep(1000) diff --git a/tests/niminaction/Chapter9/configurator/configurator.nim b/tests/niminaction/Chapter9/configurator/configurator.nim new file mode 100644 index 0000000000..0d56278896 --- /dev/null +++ b/tests/niminaction/Chapter9/configurator/configurator.nim @@ -0,0 +1,84 @@ +import macros + +proc createRefType(ident: NimIdent, identDefs: seq[NimNode]): NimNode = + result = newTree(nnkTypeSection, + newTree(nnkTypeDef, + newIdentNode(ident), + newEmptyNode(), + newTree(nnkRefTy, + newTree(nnkObjectTy, + newEmptyNode(), + newEmptyNode(), + newTree(nnkRecList, + identDefs + ) + ) + ) + ) + ) + +proc toIdentDefs(stmtList: NimNode): seq[NimNode] = + expectKind(stmtList, nnkStmtList) + result = @[] + + for child in stmtList: + expectKind(child, nnkCall) + result.add(newIdentDefs(child[0], child[1][0])) + +template constructor(ident: untyped): untyped = + proc `new ident`(): `ident` = + new result + +proc createLoadProc(typeName: NimIdent, identDefs: seq[NimNode]): NimNode = + var cfgIdent = newIdentNode("cfg") + var filenameIdent = newIdentNode("filename") + var objIdent = newIdentNode("obj") + + var body = newStmtList() + body.add quote do: + var `objIdent` = parseFile(`filenameIdent`) + + for identDef in identDefs: + let fieldNameIdent = identDef[0] + let fieldName = $fieldNameIdent.ident + case $identDef[1].ident + of "string": + body.add quote do: + `cfgIdent`.`fieldNameIdent` = `objIdent`[`fieldName`].getStr + of "int": + body.add quote do: + `cfgIdent`.`fieldNameIdent` = `objIdent`[`fieldName`].getNum().int + else: + doAssert(false, "Not Implemented") + + return newProc(newIdentNode("load"), + [newEmptyNode(), + newIdentDefs(cfgIdent, newIdentNode(typeName)), + newIdentDefs(filenameIdent, newIdentNode("string"))], + body) + +macro config*(typeName: untyped, fields: untyped): untyped = + result = newStmtList() + + let identDefs = toIdentDefs(fields) + result.add createRefType(typeName.ident, identDefs) + result.add getAst(constructor(typeName.ident)) + result.add createLoadProc(typeName.ident, identDefs) + + echo treeRepr(typeName) + echo treeRepr(fields) + + echo treeRepr(result) + echo toStrLit(result) + # TODO: Verify that we can export fields in config type so that it can be + # used in another module. + +import json +config MyAppConfig: + address: string + port: int + +var myConf = newMyAppConfig() +myConf.load("myappconfig.cfg") +echo("Address: ", myConf.address) +echo("Port: ", myConf.port) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 7b1dd0df01..f71a4a1e72 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -238,6 +238,48 @@ proc jsTests(r: var TResults, cat: Category, options: string) = for testfile in ["strutils", "json", "random", "times", "logging"]: test "lib/pure/" & testfile & ".nim" +# ------------------------- nim in action ----------- + +proc testNimInAction(r: var TResults, cat: Category, options: string) = + template test(filename: untyped, action: untyped) = + testSpec r, makeTest(filename, options, cat, action) + + template testJS(filename: untyped) = + testSpec r, makeTest(filename, options, cat, actionCompile, targetJS) + + template testCPP(filename: untyped) = + testSpec r, makeTest(filename, options, cat, actionCompile, targetCPP) + + let tests = [ + "niminaction/Chapter3/ChatApp/src/server", + "niminaction/Chapter3/ChatApp/src/client", + "niminaction/Chapter6/WikipediaStats/concurrency_regex", + "niminaction/Chapter6/WikipediaStats/concurrency", + "niminaction/Chapter6/WikipediaStats/naive", + "niminaction/Chapter6/WikipediaStats/parallel_counts", + "niminaction/Chapter6/WikipediaStats/race_condition", + "niminaction/Chapter6/WikipediaStats/sequential_counts", + "niminaction/Chapter7/Tweeter/src/tweeter", + "niminaction/Chapter7/Tweeter/src/createDatabase", + "niminaction/Chapter7/Tweeter/tests/database_test", + "niminaction/Chapter8/sdl/sdl_test", + ] + for testfile in tests: + test "tests/" & testfile & ".nim", actionCompile + + # TODO: This doesn't work for some reason ;\ + # let reject = "tests/niminaction/Chapter6/WikipediaStats" & + # "/unguarded_access.nim" + # test reject, actionReject + + let jsFile = "tests/niminaction/Chapter8/canvas/canvas_test.nim" + testJS jsFile + + let cppFile = "tests/niminaction/Chapter8/sfml/sfml_test.nim" + testCPP cppFile + + + # ------------------------- manyloc ------------------------------------------- #proc runSpecialTests(r: var TResults, options: string) = # for t in ["lib/packages/docutils/highlite"]: @@ -420,6 +462,8 @@ proc processCategory(r: var TResults, cat: Category, options: string) = testNimblePackages(r, cat, pfExtraOnly) of "nimble-all": testNimblePackages(r, cat, pfAll) + of "niminaction": + testNimInAction(r, cat, options) of "untestable": # We can't test it because it depends on a third party. discard # TODO: Move untestable tests to someplace else, i.e. nimble repo. From 02ff5f596c330b68927f843814ecb9b86c2eee67 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 1 Oct 2017 23:34:19 +0200 Subject: [PATCH 56/56] implemented new experimental scriptable import mechanism --- compiler/gorgeimpl.nim | 83 ++++++++++++++++++++++ compiler/importer.nim | 50 ++++++++++--- compiler/nimblecmd.nim | 8 +-- compiler/sem.nim | 12 ++-- compiler/vm.nim | 2 +- compiler/vmdeps.nim | 45 +----------- koch.nim | 3 + tools/nimresolve.nim | 158 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 297 insertions(+), 64 deletions(-) create mode 100644 compiler/gorgeimpl.nim create mode 100644 tools/nimresolve.nim diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim new file mode 100644 index 0000000000..4e1ce6d507 --- /dev/null +++ b/compiler/gorgeimpl.nim @@ -0,0 +1,83 @@ +# +# +# The Nim Compiler +# (c) Copyright 2017 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Module that implements ``gorge`` for the compiler as well as +## the scriptable import mechanism. + +import msgs, securehash, os, osproc, streams, strutils, options + +proc readOutput(p: Process): (string, int) = + result[0] = "" + var output = p.outputStream + while not output.atEnd: + result[0].add(output.readLine) + result[0].add("\n") + if result[0].len > 0: + result[0].setLen(result[0].len - "\n".len) + result[1] = p.waitForExit + +proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) = + let workingDir = parentDir(info.toFullPath) + if cache.len > 0:# and optForceFullMake notin gGlobalOptions: + let h = secureHash(cmd & "\t" & input & "\t" & cache) + let filename = options.toGeneratedFile("gorge_" & $h, "txt") + var f: File + if open(f, filename): + result = (f.readAll, 0) + f.close + return + var readSuccessful = false + try: + var p = startProcess(cmd, workingDir, + options={poEvalCommand, poStderrToStdout}) + if input.len != 0: + p.inputStream.write(input) + p.inputStream.close() + result = p.readOutput + readSuccessful = true + # only cache successful runs: + if result[1] == 0: + writeFile(filename, result[0]) + except IOError, OSError: + if not readSuccessful: result = ("", -1) + else: + try: + var p = startProcess(cmd, workingDir, + options={poEvalCommand, poStderrToStdout}) + if input.len != 0: + p.inputStream.write(input) + p.inputStream.close() + result = p.readOutput + except IOError, OSError: + result = ("", -1) + +proc scriptableImport*(pkg, subdir: string; info: TLineInfo): string = + var cmd = getConfigVar("resolver.exe") + if cmd.len == 0: cmd = "nimresolve" + else: cmd = quoteShell(cmd) + cmd.add " --source:" + cmd.add quoteShell(info.toFullPath()) + cmd.add " --stdlib:" + cmd.add quoteShell(options.libpath) + cmd.add " --project:" + cmd.add quoteShell(gProjectFull) + if subdir.len != 0: + cmd.add " --subdir:" + cmd.add quoteShell(subdir) + if options.gNoNimblePath: + cmd.add " --nonimblepath" + cmd.add ' ' + cmd.add quoteShell(pkg) + let (res, exitCode) = opGorge(cmd, "", cmd, info) + if exitCode == 0: + result = res.strip() + elif res.len > 0: + localError(info, res) + else: + localError(info, "cannot resolve: " & (pkg / subdir)) diff --git a/compiler/importer.nim b/compiler/importer.nim index dfc9e84ba6..07f42a1472 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -11,11 +11,22 @@ import intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, - semdata, passes, renderer + semdata, passes, renderer, gorgeimpl proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode +proc lookupPackage(pkg, subdir: PNode): string = + let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" + case pkg.kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + result = scriptableImport(pkg.strVal, sub, pkg.info) + of nkIdent: + result = scriptableImport(pkg.ident.s, sub, pkg.info) + else: + localError(pkg.info, "package name must be an identifier or string literal") + result = "" + proc getModuleName*(n: PNode): string = # This returns a short relative module name without the nim extension # e.g. like "system", "importer" or "somepath/module" @@ -31,16 +42,33 @@ proc getModuleName*(n: PNode): string = result = n.ident.s of nkSym: result = n.sym.name.s - of nkInfix, nkPrefix: - if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id: + of nkInfix: + let n0 = n[0] + let n1 = n[1] + if n0.kind == nkIdent and n0.ident.id == getIdent("as").id: # XXX hack ahead: n.kind = nkImportAs n.sons[0] = n.sons[1] n.sons[1] = n.sons[2] n.sons.setLen(2) return getModuleName(n.sons[0]) - # hacky way to implement 'x / y /../ z': - result = renderTree(n, {renderNoComments}).replace(" ") + if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$": + if n0.kind == nkIdent and n0.ident.s == "/": + result = lookupPackage(n1[1], n[2]) + else: + localError(n.info, "only '/' supported with $package notation") + result = "" + else: + # hacky way to implement 'x / y /../ z': + result = getModuleName(n1) + result.add renderTree(n0, {renderNoComments}) + result.add getModuleName(n[2]) + of nkPrefix: + if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$": + result = lookupPackage(n[1], nil) + else: + # hacky way to implement 'x / y /../ z': + result = renderTree(n, {renderNoComments}).replace(" ") of nkDotExpr: result = renderTree(n, {renderNoComments}).replace(".", "/") of nkImportAs: @@ -209,12 +237,14 @@ proc evalImport(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket: - let sep = renderTree(it.sons[0], {renderNoComments}) - let dir = renderTree(it.sons[1], {renderNoComments}) + let sep = it[0] + let dir = it[1] + let a = newNodeI(nkInfix, it.info) + a.add sep + a.add dir + a.add sep # dummy entry, replaced in the loop for x in it[2]: - let f = renderTree(x, {renderNoComments}) - let a = newStrNode(nkStrLit, (dir & sep & f).replace(" ")) - a.info = it.info + a.sons[2] = x impMod(c, a) else: impMod(c, it) diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index ab63f9e12b..39c3a17e75 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -16,11 +16,11 @@ proc addPath*(path: string, info: TLineInfo) = options.searchPaths.insert(path, 0) type - Version = distinct string + Version* = distinct string -proc `$`(ver: Version): string {.borrow.} +proc `$`*(ver: Version): string {.borrow.} -proc newVersion(ver: string): Version = +proc newVersion*(ver: string): Version = doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits, "Wrong version: " & ver) return Version(ver) @@ -28,7 +28,7 @@ proc newVersion(ver: string): Version = proc isSpecial(ver: Version): bool = return ($ver).len > 0 and ($ver)[0] == '#' -proc `<`(ver: Version, ver2: Version): bool = +proc `<`*(ver: Version, ver2: Version): bool = ## This is synced from Nimble's version module. # Handling for special versions such as "#head" or "#branch". diff --git a/compiler/sem.nim b/compiler/sem.nim index 21fd0b3836..ebfdafea72 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -495,13 +495,15 @@ proc isImportSystemStmt(n: PNode): bool = case n.kind of nkImportStmt: for x in n: - let f = checkModuleName(x, false) + if x.kind == nkIdent: + let f = checkModuleName(x, false) + if f == magicsys.systemModule.info.fileIndex: + return true + of nkImportExceptStmt, nkFromStmt: + if n[0].kind == nkIdent: + let f = checkModuleName(n[0], false) if f == magicsys.systemModule.info.fileIndex: return true - of nkImportExceptStmt, nkFromStmt: - let f = checkModuleName(n[0], false) - if f == magicsys.systemModule.info.fileIndex: - return true else: discard proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = diff --git a/compiler/vm.nim b/compiler/vm.nim index 79bfb911de..08605cad12 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -19,7 +19,7 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, - vmmarshal + vmmarshal, gorgeimpl from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 2b3cfeeeb4..29c1129b5b 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -7,50 +7,7 @@ # distribution, for details about the copyright. # -import ast, types, msgs, os, osproc, streams, options, idents, securehash - -proc readOutput(p: Process): (string, int) = - result[0] = "" - var output = p.outputStream - while not output.atEnd: - result[0].add(output.readLine) - result[0].add("\n") - if result[0].len > 0: - result[0].setLen(result[0].len - "\n".len) - result[1] = p.waitForExit - -proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) = - let workingDir = parentDir(info.toFullPath) - if cache.len > 0:# and optForceFullMake notin gGlobalOptions: - let h = secureHash(cmd & "\t" & input & "\t" & cache) - let filename = options.toGeneratedFile("gorge_" & $h, "txt") - var f: File - if open(f, filename): - result = (f.readAll, 0) - f.close - return - var readSuccessful = false - try: - var p = startProcess(cmd, workingDir, - options={poEvalCommand, poStderrToStdout}) - if input.len != 0: - p.inputStream.write(input) - p.inputStream.close() - result = p.readOutput - readSuccessful = true - writeFile(filename, result[0]) - except IOError, OSError: - if not readSuccessful: result = ("", -1) - else: - try: - var p = startProcess(cmd, workingDir, - options={poEvalCommand, poStderrToStdout}) - if input.len != 0: - p.inputStream.write(input) - p.inputStream.close() - result = p.readOutput - except IOError, OSError: - result = ("", -1) +import ast, types, msgs, os, streams, options, idents proc opSlurp*(file: string, info: TLineInfo, module: PSym): string = try: diff --git a/koch.nim b/koch.nim index 6ae45fcb7d..6b267ce461 100644 --- a/koch.nim +++ b/koch.nim @@ -262,6 +262,9 @@ proc buildTools(latest: bool) = let nimgrepExe = "bin/nimgrep".exe nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" when defined(windows): buildVccTool() + + nimexec "c -o:" & ("bin/nimresolve".exe) & " tools/nimresolve.nim" + buildNimble(latest) proc nsis(args: string) = diff --git a/tools/nimresolve.nim b/tools/nimresolve.nim new file mode 100644 index 0000000000..9af24df5a6 --- /dev/null +++ b/tools/nimresolve.nim @@ -0,0 +1,158 @@ +# +# +# The Nim Compiler +# (c) Copyright 2017 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Standard tool that resolves import paths. + +import + os, strutils, parseopt + +import "../compiler/nimblecmd" + +# You can change these constants to build you own adapted resolver. +const + considerParentDirs = not defined(noParentProjects) + considerNimbleDirs = not defined(noNimbleDirs) + +const + Version = "1.0" + Usage = "nimresolve - Nim Resolve Package Path Version " & Version & """ + + (c) 2017 Andreas Rumpf +Usage: + nimresolve [options] package +Options: + --source:FILE the file that requests to resolve 'package' + --stdlib:PATH the path to use for the standard library + --project:FILE the main '.nim' file that was passed to the Nim compiler + --subdir:EXPR the subdir part in: 'import $pkg / subdir' + --noNimblePath do not search the Nimble path to resolve the package +""" + +proc writeHelp() = + stdout.write(Usage) + stdout.flushFile() + quit(0) + +proc writeVersion() = + stdout.write(Version & "\n") + stdout.flushFile() + quit(0) + +type + Task = object + source, stdlib, subdir, project, pkg: string + noNimblePath: bool + +proc findInNimbleDir(t: Task; dir: string): bool = + var best = "" + var bestv = "" + for k, p in os.walkDir(dir, relative=true): + if k == pcDir and p.len > t.pkg.len+1 and + p[t.pkg.len] == '-' and p.startsWith(t.pkg): + let (_, a) = getPathVersion(p) + if bestv.len == 0 or bestv < a: + bestv = a + best = dir / p + + if best.len > 0: + var f: File + if open(f, best / changeFileExt(t.pkg, ".nimble-link")): + # the second line contains what we're interested in, see: + # https://github.com/nim-lang/nimble#nimble-link + var override = "" + discard readLine(f, override) + discard readLine(f, override) + close(f) + if not override.isAbsolute(): + best = best / override + else: + best = override + let f = if t.subdir.len == 0: t.pkg else: t.subdir + let res = addFileExt(best / f, "nim") + if best.len > 0 and fileExists(res): + echo res + result = true + +const stdlibDirs = [ + "pure", "core", "arch", + "pure/collections", + "pure/concurrency", "impure", + "wrappers", "wrappers/linenoise", + "windows", "posix", "js"] + +proc resolve(t: Task) = + template attempt(a) = + let x = addFileExt(a, "nim") + if fileExists(x): + echo x + return + + case t.pkg + of "stdlib": + if t.subdir.len == 0: + echo t.stdlib + return + else: + for candidate in stdlibDirs: + attempt(t.stdlib / candidate / t.subdir) + of "root": + let root = t.project.splitFile.dir + if t.subdir.len == 0: + echo root + return + else: + attempt(root / t.subdir) + else: + when considerParentDirs: + var p = parentDir(t.source.splitFile.dir) + # support 'import $karax': + let f = if t.subdir.len == 0: t.pkg else: t.subdir + + while p.len > 0: + let dir = p / t.pkg + if dirExists(dir): + attempt(dir / f) + # 2nd attempt: try to use 'karax/karax' + attempt(dir / t.pkg / f) + # 3rd attempt: try to use 'karax/src/karax' + attempt(dir / "src" / f) + attempt(dir / "src" / t.pkg / f) + p = parentDir(p) + + when considerNimbleDirs: + if not t.noNimblePath: + if findInNimbleDir(t, getHomeDir() / ".nimble" / "pkgs"): return + when not defined(windows): + if findInNimbleDir(t, "/opt/nimble/pkgs"): return + + quit "cannot resolve: " & (t.pkg / t.subdir) + +proc main = + var t: Task + t.subdir = "" + for kind, key, val in getopt(): + case kind + of cmdArgument: + t.pkg = key + of cmdLongoption, cmdShortOption: + case normalize(key) + of "source": t.source = val + of "stdlib": t.stdlib = val + of "project": t.project = val + of "subdir": t.subdir = val + of "nonimblepath": t.noNimblePath = true + of "help", "h": writeHelp() + of "version", "v": writeVersion() + else: writeHelp() + of cmdEnd: assert(false) # cannot happen + if t.pkg.len == 0: + quit "[Error] no package to resolve." + resolve(t) + +main()