From 817e4bb2fe0538a60f169d37e254b30e3dc3ab1d Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Tue, 7 Aug 2018 17:36:56 +0200 Subject: [PATCH 1/8] AsyncHttpClient: return from requests before body completion Store the body completion future at the client and wait for it to complete before issuing additional requests. This allows the body FutureStream reader to drain the stream and read buffers to be freed asynchronously. Fix #8109 --- lib/pure/httpclient.nim | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 8b4fb0f8c1..72de727184 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -807,6 +807,7 @@ type lastProgressReport: float when SocketType is AsyncSocket: bodyStream: FutureStream[string] + parseBodyFut: Future[void] else: bodyStream: Stream getBody: bool ## When `false`, the body is never read in requestAux. @@ -1066,10 +1067,14 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, if getBody: when client is HttpClient: client.bodyStream = newStringStream() + result.bodyStream = client.bodyStream + parseBody(client, result.headers, result.version) else: client.bodyStream = newFutureStream[string]("parseResponse") - await parseBody(client, result.headers, result.version) - result.bodyStream = client.bodyStream + result.bodyStream = client.bodyStream + assert(client.parseBodyFut.isNil or client.parseBodyFut.finished) + client.parseBodyFut = parseBody(client, result.headers, result.version) + # do not wait here for the body request to complete proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = @@ -1159,6 +1164,12 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string, # Helper that actually makes the request. Does not handle redirects. let requestUrl = parseUri(url) + when client is AsyncHttpClient: + if not client.parseBodyFut.isNil: + # let the current operation finish before making another request + await client.parseBodyFut + client.parseBodyFut = nil + await newConnection(client, requestUrl) let effectiveHeaders = client.headers.override(headers) From 32b62097a2636c5053d842939d34e2a0fa3f9e33 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 8 Aug 2018 15:34:21 +0200 Subject: [PATCH 2/8] Fix regression for mapIt (#8567) Don't try to be too smart and limit the use of `evalOnce` where strictly needed as not every value can be assigned with a `let`. Fixes #8566 --- lib/pure/collections/sequtils.nim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 8f81fe4f50..99ce91979d 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -676,8 +676,8 @@ template mapIt*(s, op: untyped): untyped = var it{.inject.}: type(items(s)); op)) var result: seq[outType] - evalOnce(t, s) - when compiles(t.len): + when compiles(s.len): + evalOnce(t, s) var i = 0 result = newSeq[outType](t.len) for it {.inject.} in t: @@ -685,7 +685,7 @@ template mapIt*(s, op: untyped): untyped = i += 1 else: result = @[] - for it {.inject.} in t: + for it {.inject.} in s: result.add(op) result @@ -1071,5 +1071,10 @@ when isMainModule: proc foo(x: openArray[int]): seq[int] = x.mapIt(it + 1) doAssert foo([1,2,3]) == @[2,3,4] + block: # mapIt with invalid RHS for `let` (#8566) + type X = enum + A, B + doAssert mapIt(X, $it) == @["A", "B"] + when not defined(testing): echo "Finished doc tests" From 506418ef539a9142307235c0cd0183f0d7aec749 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 8 Aug 2018 07:47:17 -0700 Subject: [PATCH 3/8] add build_all.sh to allow 1-liner to build development version of the compiler (#8546) --- build_all.sh | 22 ++++++++++++++++++++++ readme.md | 25 +++++++++++++++++-------- 2 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 build_all.sh diff --git a/build_all.sh b/build_all.sh new file mode 100644 index 0000000000..cd5ea3d59b --- /dev/null +++ b/build_all.sh @@ -0,0 +1,22 @@ +#! /bin/sh + +# build development version of the compiler; can be rerun safely + +set -u # error on undefined variables +set -e # exit on first error + +echo_run(){ + echo "\n$@" + "$@" +} + +[ -d csources ] || echo_run git clone --depth 1 https://github.com/nim-lang/csources.git +( + ## avoid changing dir in case of failure + echo_run cd csources + echo_run sh build.sh +) + +echo_run bin/nim c koch +echo_run ./koch boot -d:release +echo_run ./koch tools # Compile Nimble and other tools. diff --git a/readme.md b/readme.md index bdc7c7549e..e4fb52bd01 100644 --- a/readme.md +++ b/readme.md @@ -50,24 +50,33 @@ Next, to build from source you will need: other distros as well). Then, if you are on a \*nix system or Windows, the following steps should compile -Nim from source using ``gcc``, ``git`` and the ``koch`` build tool (in the place -of ``sh build.sh`` you should substitute ``build.bat`` on x86 Windows or -``build64.bat`` on x86_64 Windows): +Nim from source using ``gcc``, ``git`` and the ``koch`` build tool. **Note: The following commands are for the development version of the compiler.** For most users, installing the latest stable version is enough. Check out the installation instructions on the website to do so: https://nim-lang.org/install.html. ``` +# step 1: git clone https://github.com/nim-lang/Nim.git cd Nim + +# step 2 (posix) clones `csources.git`, bootstraps Nim compiler and compiles tools +sh build_all.sh + +# step 2 (windows) git clone --depth 1 https://github.com/nim-lang/csources.git + cd csources -sh build.sh -cd ../ -bin/nim c koch -./koch boot -d:release -./koch tools # Compile Nimble and other tools. +# requires `gcc` in your PATH, see also https://nim-lang.org/install_windows.html +build.bat # x86 Windows +build64.bat # x86_64 Windows +cd .. + +bin\nim c koch +koch boot -d:release +koch tools # Compile Nimble and other tools +# end of step 2 (windows) ``` Finally, once you have finished the build steps (on Windows, Mac or Linux) you From bccaa36aba64d2424ceedc9723d5ec01534dde94 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Wed, 8 Aug 2018 17:49:33 +0200 Subject: [PATCH 4/8] Tests for v1 closed generics/static issues (#8572) * Add tests to confirm https://github.com/nim-lang/Nim/issues/7231 is fixed. * Add test for closed https://github.com/nim-lang/Nim/issues/6137 * Add test for https://github.com/nim-lang/Nim/issues/7141 --- tests/generics/t6137.nim | 29 +++++++++++++++++++++++++++++ tests/generics/t7141.nim | 10 ++++++++++ tests/metatype/ttypeselectors.nim | 12 ++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 tests/generics/t6137.nim create mode 100644 tests/generics/t7141.nim diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim new file mode 100644 index 0000000000..639675f35d --- /dev/null +++ b/tests/generics/t6137.nim @@ -0,0 +1,29 @@ +discard """ + action: "reject" + line: 29 + errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters." +""" + +type + # simple vector of declared fixed length + vector[N : static[int]] = array[0..N-1, float] + +proc `*`[T](x: float, a: vector[T]): vector[T] = + # multiplication by scalar + for ii in 0..high(a): + result[ii] = a[ii]*x + +let + # define a vector of length 3 + x: vector[3] = [1.0, 3.0, 5.0] + +proc vectFunc[T](x: vector[T]): vector[T] {.procvar.} = + # Define a vector function + result = 2.0*x + +proc passVectFunction[T](g: proc(x: vector[T]): vector[T], x: vector[T]): vector[T] = + # pass a vector function as input in another procedure + result = g(x) + +let + xNew = passVectFunction(vectFunc,x) diff --git a/tests/generics/t7141.nim b/tests/generics/t7141.nim new file mode 100644 index 0000000000..8a128d828a --- /dev/null +++ b/tests/generics/t7141.nim @@ -0,0 +1,10 @@ +discard """ + action: "reject" + line: 7 + errormsg: "cannot instantiate: \'T\'" +""" + +proc foo[T](x: T) = + discard + +var fun = if true: foo else: foo diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 2a2455adb5..eb857271dd 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -99,3 +99,15 @@ echo sizeof(a) echo sizeof(b) echo sizeof(c) +# This is the same example but using a proc instead of a macro +# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone) +# https://github.com/nim-lang/Nim/issues/7231 + +proc getBase2*(bits: static[int]): typedesc = + if bits == 128: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64")) + else: + result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32")) + +type + MpUint2*[bits: static[int]] = getbase2(bits) From 98225ca207e4057c1da7966430d0699c0151e3e7 Mon Sep 17 00:00:00 2001 From: Grant Date: Thu, 9 Aug 2018 00:40:21 -0700 Subject: [PATCH 5/8] Update channels.nim (#8583) Fix typo in channels.nim --- lib/system/channels.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/channels.nim b/lib/system/channels.nim index 254b87dfcc..14d3a30055 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -233,7 +233,7 @@ proc send*[TMsg](c: var Channel[TMsg], msg: TMsg) {.inline.} = proc trySend*[TMsg](c: var Channel[TMsg], msg: TMsg): bool {.inline.} = ## Tries to send a message to a thread. `msg` is deeply copied. Doesn't block. ## Returns `false` if the message was not sent because number of pending items - ## in the cannel exceeded `maxItems`. + ## in the channel exceeded `maxItems`. sendImpl(cast[PRawChannel](addr c), cast[PNimType](getTypeInfo(msg)), unsafeAddr(msg), true) proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) = From a2c5ffba0cd82b5677fa57612f7dcb7af2348f45 Mon Sep 17 00:00:00 2001 From: alaviss Date: Thu, 9 Aug 2018 15:50:36 +0700 Subject: [PATCH 6/8] Uses XDG_CACHE_HOME if available (#8585) * compiler/options: use XDG_CACHE_HOME if set * doc/nimc: update documentation --- compiler/options.nim | 2 +- doc/nimc.rst | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index be9342085b..44519ea22e 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -482,7 +482,7 @@ include packagehandling proc getOsCacheDir(): string = when defined(posix): - result = getHomeDir() / ".cache" + result = string getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") else: result = getHomeDir() / genSubDir diff --git a/doc/nimc.rst b/doc/nimc.rst index 277c88ec11..fca5b273cf 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -159,7 +159,8 @@ Generated C code directory The generated files that Nim produces all go into a subdirectory called ``nimcache``. Its full path is -- ``~/.cache/$projectname(_r|_d)`` on Posix +- ``$XDG_CACHE_HOME/$projectname(_r|_d)`` or ``~/.cache/$projectname(_r|_d)`` + on Posix - ``$HOME/nimcache/$projectname(_r|_d)`` on Windows. The ``_r`` suffix is used for release builds, ``_d`` is for debug builds. From 730ce53b71a207edf93abe09c14c150b9c360028 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 9 Aug 2018 01:56:05 -0700 Subject: [PATCH 7/8] save a backup of csources-built nim to bin/nim_csources to avoid recompiling from csources (#8582) --- build_all.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/build_all.sh b/build_all.sh index cd5ea3d59b..701d7d2042 100644 --- a/build_all.sh +++ b/build_all.sh @@ -11,11 +11,20 @@ echo_run(){ } [ -d csources ] || echo_run git clone --depth 1 https://github.com/nim-lang/csources.git -( + +nim_csources=bin/nim_csources +build_nim_csources(){ ## avoid changing dir in case of failure - echo_run cd csources - echo_run sh build.sh -) + ( + echo_run cd csources + echo_run sh build.sh + ) + # keep $nim_csources in case needed to investigate bootstrap issues + # without having to rebuild from csources + echo_run cp bin/nim $nim_csources +} + +[ -f $nim_csources ] || echo_run build_nim_csources echo_run bin/nim c koch echo_run ./koch boot -d:release From 43f634db8dfac4fb13c8454958d35e776410dac1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 10 Aug 2018 00:20:14 -0700 Subject: [PATCH 8/8] fixes #8519; implements T.distinctBase to reverse T = distinct A (#8531) --- doc/manual.rst | 3 ++- lib/pure/sugar.nim | 38 ++++++++++++++++++++++++++++++++++++++ tests/stdlib/tsugar.nim | 29 +++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/tsugar.nim diff --git a/doc/manual.rst b/doc/manual.rst index abdc4ce695..de0a131434 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1673,7 +1673,8 @@ A ``distinct`` type is new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its -base type and vice versa are allowed. +base type and vice versa are allowed. See also ``distinctBase`` to get the +reverse operation. Modelling currencies diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 258b40191a..8ded552d9d 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -198,3 +198,41 @@ macro dump*(x: typed): untyped = let r = quote do: debugEcho `s`, " = ", `x` return r + +# TODO: consider exporting this in macros.nim +proc freshIdentNodes(ast: NimNode): NimNode = + # Replace NimIdent and NimSym by a fresh ident node + # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 + proc inspect(node: NimNode): NimNode = + case node.kind: + of nnkIdent, nnkSym: + result = ident($node) + of nnkEmpty, nnkLiterals: + result = node + else: + result = node.kind.newTree() + for child in node: + result.add inspect(child) + result = inspect(ast) + +macro distinctBase*(T: typedesc): untyped = + ## reverses ``type T = distinct A``; works recursively. + runnableExamples: + type T = distinct int + doAssert distinctBase(T) is int + doAssert: not compiles(distinctBase(int)) + type T2 = distinct T + doAssert distinctBase(T2) is int + + let typeNode = getTypeImpl(T) + expectKind(typeNode, nnkBracketExpr) + if typeNode[0].typeKind != ntyTypeDesc: + error "expected typeDesc, got " & $typeNode[0] + var typeSym = typeNode[1] + typeSym = getTypeImpl(typeSym) + if typeSym.typeKind != ntyDistinct: + error "type is not distinct" + typeSym = typeSym[0] + while typeSym.typeKind == ntyDistinct: + typeSym = getTypeImpl(typeSym)[0] + typeSym.freshIdentNodes diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim new file mode 100644 index 0000000000..a870bf6fed --- /dev/null +++ b/tests/stdlib/tsugar.nim @@ -0,0 +1,29 @@ +discard """ + file: "tsugar.nim" + output: "" +""" +import sugar +import macros + +block distinctBase: + block: + type + Foo[T] = distinct seq[T] + var a: Foo[int] + doAssert a.type.distinctBase is seq[int] + + block: + # simplified from https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458 + macro uintImpl(bits: static[int]): untyped = + if bits >= 128: + let inner = getAST(uintImpl(bits div 2)) + result = newTree(nnkBracketExpr, ident("UintImpl"), inner) + else: + result = ident("uint64") + + type + BaseUint = UintImpl or SomeUnsignedInt + UintImpl[Baseuint] = object + Uint[bits: static[int]] = distinct uintImpl(bits) + + doAssert Uint[128].distinctBase is UintImpl[uint64]