From 8310d252c27219f97980aff501b6ddd6de16eab2 Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Mon, 20 Mar 2017 21:20:26 +0200 Subject: [PATCH 01/29] Update testament to include all tests from tests/threads category. (#5576) --- tests/testament/categories.nim | 24 +++++------------------- tests/threads/trecursive_actor.nim | 1 + tests/threads/tthreadanalysis.nim | 1 + tests/threads/ttryrecv.nim | 1 + 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 0685dd73a7..4ba07cd212 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -186,25 +186,11 @@ proc longGCTests(r: var TResults, cat: Category, options: string) = proc threadTests(r: var TResults, cat: Category, options: string) = template test(filename: untyped) = - testSpec r, makeTest("tests/threads" / filename, options, cat, actionRun) - testSpec r, makeTest("tests/threads" / filename, options & - " -d:release", cat, actionRun) - testSpec r, makeTest("tests/threads" / filename, options & - " --tlsEmulation:on", cat, actionRun) - - test "tactors" - test "tactors2" - test "threadex" - # deactivated because output capturing still causes problems sometimes: - #test "trecursive_actor" - #test "threadring" - #test "tthreadanalysis" - #test "tthreadsort" - test "tthreadanalysis2" - #test "tthreadanalysis3" - test "tthreadheapviolation1" - test "tonthreadcreation" - test "tracy_allocator" + testSpec r, makeTest(filename, options, cat, actionRun) + testSpec r, makeTest(filename, options & " -d:release", cat, actionRun) + testSpec r, makeTest(filename, options & " --tlsEmulation:on", cat, actionRun) + for t in os.walkFiles("tests/threads/t*.nim"): + test(t) # ------------------------- IO tests ------------------------------------------ diff --git a/tests/threads/trecursive_actor.nim b/tests/threads/trecursive_actor.nim index e2774704c4..d7072aa531 100644 --- a/tests/threads/trecursive_actor.nim +++ b/tests/threads/trecursive_actor.nim @@ -1,4 +1,5 @@ discard """ + disabled: yes outputsub: "0" """ diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim index a6a847a0af..5b0b666ac7 100644 --- a/tests/threads/tthreadanalysis.nim +++ b/tests/threads/tthreadanalysis.nim @@ -1,4 +1,5 @@ discard """ + disabled: yes outputsub: "101" errormsg: "'threadFunc' is not GC-safe" line: 39 diff --git a/tests/threads/ttryrecv.nim b/tests/threads/ttryrecv.nim index 4a98e6c272..5ef1a3c10f 100644 --- a/tests/threads/ttryrecv.nim +++ b/tests/threads/ttryrecv.nim @@ -1,4 +1,5 @@ discard """ + disabled: yes outputsub: "channel is empty" """ From d862d22723885e2d0f9ac9b58cafcb30e191cda2 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 21 Mar 2017 12:03:59 +0100 Subject: [PATCH 02/29] koch: vcc is also built with 'koch tools' --- koch.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index f5e4d3ee3e..6eee09b7bc 100644 --- a/koch.nim +++ b/koch.nim @@ -218,11 +218,14 @@ proc bundleNimsuggest(buildExe: bool) = copyExe("nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe) removeFile("nimsuggest/nimsuggest".exe) +proc buildVccTool() = + nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe") + proc bundleWinTools() = nimexec("c tools/finish.nim") copyExe("tools/finish".exe, "finish".exe) removeFile("tools/finish".exe) - nimexec("c -o:bin/vccexe.exe tools/vccenv/vccexe") + buildVccTool() nimexec("c -o:bin/nimgrab.exe -d:ssl tools/nimgrab.nim") when false: # not yet a tool worth including @@ -257,6 +260,7 @@ proc buildTools(latest: bool) = let nimgrepExe = "bin/nimgrep".exe nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" + when defined(windows): buildVccTool() buildNimble(latest) proc nsis(args: string) = From e01d2244e41603e0818dea8a98f4b23f9b3cf387 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 21 Mar 2017 13:02:38 +0100 Subject: [PATCH 03/29] make semObjConstr robust for nimsuggest --- compiler/semexprs.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 32f74e0509..39113079ab 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2084,9 +2084,8 @@ proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) - result = n - result.typ = t - result.kind = nkObjConstr + result = newNodeIT(nkObjConstr, n.info, t) + result.add n.sons[0] t = skipTypes(t, {tyGenericInst, tyAlias}) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) if t.kind != tyObject: @@ -2125,6 +2124,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = else: localError(it.info, errUndeclaredFieldX, id.s) it.sons[1] = e + result.add it # XXX object field name check for 'case objects' if the kind is static? if tfNeedsInit in objType.flags: while true: From 3962c6339f8fdc65721ad640776881d58c6329e8 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 21 Mar 2017 13:09:57 +0100 Subject: [PATCH 04/29] nimsuggest: die with a project dir/file that doesn't exist --- nimsuggest/nimsuggest.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 0b66dfb404..329e09510d 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -485,6 +485,10 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) = incl gGlobalOptions, optCaasEnabled isServing = true wantMainModule() + + if not fileExists(gProjectFull): + quit "cannot find file: " & gProjectFull + add(searchPaths, options.libpath) # do not stop after the first error: From c9b86ebce819331d5f8c0f2fef745dc626917e41 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 21 Mar 2017 13:17:23 +0100 Subject: [PATCH 05/29] koch: cleanup its help output, remove cruft --- koch.nim | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/koch.nim b/koch.nim index 6eee09b7bc..e24367dd50 100644 --- a/koch.nim +++ b/koch.nim @@ -41,30 +41,26 @@ Options: Possible Commands: boot [options] bootstraps with given command line options distrohelper [bindir] helper for distro packagers - geninstall generate ./install.sh; Unix only! - testinstall test tar.xz package; Unix only! Only for devs! - clean cleans Nim project; removes generated files - web [options] generates the website and the full documentation - website [options] generates only the website - csource [options] builds the C sources for installation - pdf builds the PDF documentation - zip builds the installation ZIP package - xz builds the installation XZ package - nsis [options] builds the NSIS Setup installer (for Windows) - tests [options] run the testsuite - temp options creates a temporary compiler for testing - winrelease creates a release (for coredevs only) - nimble builds the Nimble tool tools builds Nim related tools - pushcsource push generated C sources to its repo! Only for devs! + nimble builds the Nimble tool Boot options: -d:release produce a release version of the compiler - -d:tinyc include the Tiny C backend (not supported on Windows) -d:useLinenoise use the linenoise library for interactive mode (not needed on Windows) - -d:nativeStacktrace use native stack traces (only for Mac OS X or Linux) - -d:noCaas build Nim without CAAS support -d:avoidTimeMachine only for Mac OS X, excludes nimcache dir from backups + +Commands for core developers: + web [options] generates the website and the full documentation + website [options] generates only the website + csource -d:release builds the C sources for installation + pdf builds the PDF documentation + zip builds the installation zip package + xz builds the installation tar.xz package + testinstall test tar.xz package; Unix only! + tests [options] run the testsuite + temp options creates a temporary compiler for testing + winrelease creates a Windows release + pushcsource push generated C sources to its repo Web options: --googleAnalytics:UA-... add the given google analytics code to the docs. To build the official docs, use UA-48159761-1 From c8954b2b34becebb09a3c69181094fb42f696bec Mon Sep 17 00:00:00 2001 From: jlp765 Date: Wed, 22 Mar 2017 10:04:37 +1000 Subject: [PATCH 06/29] tut1.rst: Slices indices explanation (#5569) --- doc/tut1.rst | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index 65906376e2..06ee84c0db 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -407,7 +407,7 @@ or ... Other useful iterators for collections (like arrays and sequences) are -* ``items`` and ``mitems``, which provides immutable and mutable elements respectively, and +* ``items`` and ``mitems``, which provides immutable and mutable elements respectively, and * ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively) .. code-block:: nim @@ -1394,6 +1394,27 @@ slice's bounds can hold any value supported by their type, but it is the proc using the slice object which defines what values are accepted. + To understand some of the different ways of specifying the indices of strings, arrays, sequences, etc., + it must be remembered that Nim uses zero-based indices. + + So the string ``b`` is of length 19, and two different ways of specifying the indices are + + .. code-block:: nim + + "Slices are useless." + | | | + 0 11 17 using indices + ^19 ^8 ^2 using ^ syntax + + where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0.. Date: Wed, 22 Mar 2017 07:06:05 +0700 Subject: [PATCH 07/29] Reduce the scope of stackTrace var (#5583) Fixes #5571. --- lib/pure/unittest.nim | 3 +-- tests/stdlib/tunittest.nim | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 5639689609..1ea7b8545f 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -375,7 +375,6 @@ template test*(name, body) {.dirty.} = ensureFormattersInitialized() if shouldRun(name): - var stackTrace {.inject.}: string checkpoints = @[] var testStatusIMPL {.inject.} = OK @@ -391,7 +390,7 @@ template test*(name, body) {.dirty.} = except: when not defined(js): checkpoint("Unhandled exception: " & getCurrentExceptionMsg()) - stackTrace = getCurrentException().getStackTrace() + var stackTrace {.inject.} = getCurrentException().getStackTrace() fail() finally: diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 3f8601323e..674ce50ddc 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -96,5 +96,12 @@ suite "bug #4494": check: allIt(0..3, tags[it] != tags[it + 1]) +suite "bug #5571": + test "can define gcsafe procs within tests": + proc doTest {.gcsafe.} = + let line = "a" + check: line == "a" + doTest() + static: echo "compile end" From 70237e1fdd966c06f7ef4a4211456c27c4d644e7 Mon Sep 17 00:00:00 2001 From: andri lim Date: Wed, 22 Mar 2017 14:40:15 +0700 Subject: [PATCH 08/29] fix taliasinequality test case (#5587) --- tests/types/taliasinequality.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/types/taliasinequality.nim b/tests/types/taliasinequality.nim index c64933fa2f..f3ecd536ab 100644 --- a/tests/types/taliasinequality.nim +++ b/tests/types/taliasinequality.nim @@ -52,13 +52,13 @@ test(OrderR, OrderG) test(OrderR, OrderAlias) test(OrderG, OrderAlias) -typeRep(OrderAlias, Order) # true -typeRep(OrderR, Order) # true -typeRep(OrderG, Order) # true +typeRep(OrderAlias.R, Order.R) # true +typeRep(OrderR.R, Order.R) # true +typeRep(OrderG.R, Order.R) # true -typeRep(OrderR, OrderAlias) # true -typeRep(OrderG, OrderAlias) # true -typeRep(OrderR, OrderG) # true +typeRep(OrderR.R, OrderAlias.R) # true +typeRep(OrderG.R, OrderAlias.R) # true +typeRep(OrderR.R, OrderG.R) # true echo OrderR.R # R echo OrderG.R # R From be174fc3c731f1aecf07d7750c038dbb7a018812 Mon Sep 17 00:00:00 2001 From: zah Date: Thu, 23 Mar 2017 13:40:57 +0200 Subject: [PATCH 09/29] Fix generic forward declarations; fixes #4104; fixes #4908 (#5566) --- compiler/astalgo.nim | 2 +- compiler/semexprs.nim | 1 - compiler/seminst.nim | 14 +++++++++++--- tests/generics/tforward_generic.nim | 16 ++++++++-------- tests/generics/tforwardgeneric.nim | 18 ++++++++++++++++-- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 161e4d6372..77108eb7ba 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -82,7 +82,7 @@ template mdbg*: bool {.dirty.} = elif compiles(L.fileIdx): L.fileIdx == gProjectMainIdx else: - false + error() # --------------------------- ident tables ---------------------------------- proc idTableGet*(t: TIdTable, key: PIdObj): RootRef diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 39113079ab..4d698dbfc9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1433,7 +1433,6 @@ proc semReturn(c: PContext, n: PNode): PNode = proc semProcBody(c: PContext, n: PNode): PNode = openScope(c) - result = semExpr(c, n) if c.p.resultSym != nil and not isEmptyType(result.typ): # transform ``expr`` to ``result = expr``, but not if the expr is already diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 78dd7efe53..71752f5c35 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -125,6 +125,11 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = if n.sons[bodyPos].kind != nkEmpty: + let procParams = result.typ.n + for i in 1 .. Date: Thu, 23 Mar 2017 07:27:16 -0500 Subject: [PATCH 10/29] fix empty link file list during external compilation (#5577) --- compiler/extccomp.nim | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 70cd411fef..1af113be62 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -785,14 +785,24 @@ proc writeJsonBuildInstructions*(projectfile: string) = else: lit "],\L" - proc linkfiles(f: File; buf, objfiles: var string; toLink: seq[string]) = - for i, it in toLink: - let objfile = addFileExt(it, CC[cCompiler].objExt) - str(objfile) + proc linkfiles(f: File; buf, objfiles: var string) = + for i, it in externalToLink: + let + objFile = if noAbsolutePaths(): it.extractFilename else: it + objStr = addFileExt(objFile, CC[cCompiler].objExt) add(objfiles, ' ') - add(objfiles, quoteShell(objfile)) - - if i == toLink.high: + add(objfiles, objStr) + str objStr + if toCompile.len == 0 and i == externalToLink.high: + lit "\L" + else: + lit ",\L" + for i, x in toCompile: + let objStr = quoteShell(x.obj) + add(objfiles, ' ') + add(objfiles, objStr) + str objStr + if i == toCompile.high: lit "\L" else: lit ",\L" @@ -809,7 +819,7 @@ proc writeJsonBuildInstructions*(projectfile: string) = lit "],\L\"link\":[\L" var objfiles = "" # XXX add every file here that is to link - linkfiles(f, buf, objfiles, externalToLink) + linkfiles(f, buf, objfiles) lit "],\L\"linkcmd\": " str getLinkCmd(projectfile, objfiles) From 254fbcc548247865cf15200a200436024258e647 Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Thu, 23 Mar 2017 17:13:38 +0200 Subject: [PATCH 11/29] Fixes #4719. (#5585) --- lib/system/alloc.nim | 3 +++ lib/system/threads.nim | 42 ++++++++++++++++++++---------------- tests/threads/tmanyjoin.nim | 30 ++++++++++++++++++++++++++ tests/threads/treusetvar.nim | 28 ++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 tests/threads/tmanyjoin.nim create mode 100644 tests/threads/treusetvar.nim diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index cde17beadb..edd8ececbe 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -763,6 +763,8 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} = # ---------------------- thread memory region ------------------------------- template instantiateForRegion(allocator: untyped) = + {.push stackTrace: off.} + when defined(fulldebug): proc interiorAllocatedPtr*(p: pointer): pointer = result = interiorAllocatedPtr(allocator, p) @@ -850,5 +852,6 @@ template instantiateForRegion(allocator: untyped) = proc getOccupiedSharedMem(): int = sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem) + {.pop.} {.pop.} diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 5cdca5470a..dc9cc96562 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -242,7 +242,6 @@ type PGcThread = ptr GcThread GcThread {.pure, inheritable.} = object - sys: SysThread when emulatedThreadVars and not useStackMaskHack: tls: ThreadLocalStorage else: @@ -345,18 +344,16 @@ when not defined(useNimRtl): # use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. type - Thread* {.pure, final.}[TArg] = - object of GcThread ## Nim thread. A thread is a heavy object (~14K) - ## that **must not** be part of a message! Use - ## a ``ThreadId`` for that. + Thread* {.pure, final.}[TArg] = object + core: PGcThread + sys: SysThread when TArg is void: dataFn: proc () {.nimcall, gcsafe.} else: dataFn: proc (m: TArg) {.nimcall, gcsafe.} data: TArg - ThreadId*[TArg] = ptr Thread[TArg] ## the current implementation uses - ## a pointer as a thread ID. -{.deprecated: [TThread: Thread, TThreadId: ThreadId].} + +{.deprecated: [TThread: Thread].} var threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}] @@ -423,19 +420,20 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = when declared(threadType): threadType = ThreadType.NimThread when declared(registerThread): - thrd.stackBottom = addr(thrd) - registerThread(thrd) + thrd.core.stackBottom = addr(thrd) + registerThread(thrd.core) p(thrd) - when declared(registerThread): unregisterThread(thrd) + when declared(registerThread): unregisterThread(thrd.core) when declared(deallocOsPages): deallocOsPages() else: threadProcWrapDispatch(thrd) template threadProcWrapperBody(closure: expr) {.immediate.} = - when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) + var thrd = cast[ptr Thread[TArg]](closure) + var core = thrd.core + when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) when declared(initAllocator): initAllocator() - var thrd = cast[ptr Thread[TArg]](closure) threadProcWrapStackFrame(thrd) # Since an unhandled exception terminates the whole process (!), there is # no need for a ``try finally`` here, nor would it be correct: The current @@ -444,7 +442,9 @@ template threadProcWrapperBody(closure: expr) {.immediate.} = # page! # mark as not running anymore: + thrd.core = nil thrd.dataFn = nil + deallocShared(cast[pointer](core)) {.push stack_trace:off.} when defined(windows): @@ -502,6 +502,10 @@ when false: discard pthread_cancel(t.sys) when declared(registerThread): unregisterThread(addr(t)) t.dataFn = nil + ## if thread `t` already exited, `t.core` will be `null`. + if not isNil(t.core): + deallocShared(t.core) + t.core = nil when hostOS == "windows": proc createThread*[TArg](t: var Thread[TArg], @@ -510,9 +514,11 @@ when hostOS == "windows": ## creates a new thread `t` and starts its execution. Entry point is the ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you ## don't need to pass any data to the thread. + t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) + when TArg isnot void: t.data = param t.dataFn = tp - when hasSharedHeap: t.stackSize = ThreadStackSize + when hasSharedHeap: t.core.stackSize = ThreadStackSize var dummyThreadId: int32 t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], addr(t), 0'i32, dummyThreadId) @@ -532,9 +538,11 @@ else: ## creates a new thread `t` and starts its execution. Entry point is the ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you ## don't need to pass any data to the thread. + t.core = cast[PGcThread](allocShared0(sizeof(GcThread))) + when TArg isnot void: t.data = param t.dataFn = tp - when hasSharedHeap: t.stackSize = ThreadStackSize + when hasSharedHeap: t.core.stackSize = ThreadStackSize var a {.noinit.}: PthreadAttr pthread_attr_init(a) pthread_attr_setstacksize(a, ThreadStackSize) @@ -554,10 +562,6 @@ else: proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) = createThread[void](t, tp) -proc threadId*[TArg](t: var Thread[TArg]): ThreadId[TArg] {.inline.} = - ## returns the thread ID of `t`. - result = addr(t) - when false: proc mainThreadId*[TArg](): ThreadId[TArg] = ## returns the thread ID of the main thread. diff --git a/tests/threads/tmanyjoin.nim b/tests/threads/tmanyjoin.nim new file mode 100644 index 0000000000..2c1cda4947 --- /dev/null +++ b/tests/threads/tmanyjoin.nim @@ -0,0 +1,30 @@ +discard """ + outputsub: "129" +""" + +import os, locks + +type + MarkerObj = object + lock: Lock + counter: int + Marker = ptr MarkerObj + +const + ThreadsCount = 129 + SleepTime = 1000 + +proc worker(p: Marker) {.thread.} = + acquire(p.lock) + inc(p.counter) + release(p.lock) + sleep(SleepTime) + +var p = cast[Marker](allocShared0(sizeof(MarkerObj))) +initLock(p.lock) +var ts = newSeq[Thread[Marker]](ThreadsCount) +for i in 0.. Date: Thu, 23 Mar 2017 16:43:10 +0100 Subject: [PATCH 12/29] fixes #5598 --- compiler/cgen.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c829a463e5..ebdbffa5a2 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -604,6 +604,7 @@ proc generateHeaders(m: BModule) = else: addf(m.s[cfsHeaders], "#include $1$N", [rope(it)]) add(m.s[cfsHeaders], "#undef linux" & tnl) + add(m.s[cfsHeaders], "#undef near" & tnl) proc initFrame(p: BProc, procname, filename: Rope): Rope = discard cgsym(p.module, "nimFrame") From ab31b776ef00df4a43f8afccb2e7c39a05616d2a Mon Sep 17 00:00:00 2001 From: def Date: Thu, 23 Mar 2017 21:29:10 +0100 Subject: [PATCH 13/29] Fix typo --- compiler/main.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/main.nim b/compiler/main.nim index 5f86e61881..f662ded1ba 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -272,7 +272,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = if msgs.gErrorCounter == 0 and gCmd notin {cmdInterpret, cmdRun, cmdDump}: when declared(system.getMaxMem): - let usedMem = formatSize(getMaxMem()) & " peekmem" + let usedMem = formatSize(getMaxMem()) & " peakmem" else: let usedMem = formatSize(getTotalMem()) rawMessage(hintSuccessX, [$gLinesCompiled, From d221e6a35c62b892867b96aaba7ef94a29d5e468 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 23 Mar 2017 21:34:03 +0100 Subject: [PATCH 14/29] Improve os.getFileSize documentation to specify the unit. --- lib/pure/os.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 07670c5dac..39cdf6d81b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1616,7 +1616,8 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = - ## returns the file size of `file`. Can raise ``OSError``. + ## returns the file size of `file` (in bytes). An ``OSError`` exception is + ## raised in case of an error. when defined(windows): var a: WIN32_FIND_DATA var resA = findFirstFile(file, a) From f2ca6021dc3dae51d98fbe830c1590b675522800 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 23 Mar 2017 21:34:32 +0100 Subject: [PATCH 15/29] Implement requestAnimationFrame and cancelAnimationFrame in dom module. --- lib/js/dom.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 5104712e8c..5a33dfa9f2 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -406,6 +406,8 @@ proc setInterval*(w: Window, function: proc (), pause: int): ref TInterval proc setTimeout*(w: Window, code: cstring, pause: int): ref TTimeOut proc setTimeout*(w: Window, function: proc (), pause: int): ref TInterval proc stop*(w: Window) +proc requestAnimationFrame*(w: Window, function: proc (time: float)): int +proc cancelAnimationFrame*(w: Window, id: int) # Node "methods" proc appendChild*(n, child: Node) From 0cad2896ae7aa7d9b2561785f9243ceb546abf99 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 23 Mar 2017 21:34:53 +0100 Subject: [PATCH 16/29] Implement asyncfile.readToStream. --- lib/pure/asyncfile.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 488b8276eb..c58e6c11be 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -489,3 +489,13 @@ proc writeFromStream*(f: AsyncFile, fs: FutureStream[string]) {.async.} = await f.write(value) else: break + +proc readToStream*(f: AsyncFile, fs: FutureStream[string]) {.async.} = + ## Writes data to the specified future stream as the file is read. + while true: + let data = await read(f, 4000) + if data.len == 0: + break + await fs.write(data) + + fs.complete() \ No newline at end of file From 568c954062c203383be0073126b2e7090721364f Mon Sep 17 00:00:00 2001 From: andri lim Date: Fri, 24 Mar 2017 05:39:29 +0700 Subject: [PATCH 17/29] fixes #5241, fixes #5411 inherit from specialized generic typeRel problem (#5573) --- compiler/sigmatch.nim | 34 +++++++++++----- tests/generics/tobjecttyperel.nim | 65 +++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 tests/generics/tobjecttyperel.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ae7be3c6d7..164fd09990 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -350,6 +350,14 @@ proc handleFloatRange(f, a: PType): TTypeRelation = else: result = isIntConv else: result = isNone +proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = + if fGenericOrigin != nil and last.kind == tyGenericInst and + last.len-1 == fGenericOrigin.len: + for i in countup(1, sonsLen(fGenericOrigin) - 1): + let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i])) + if x == nil: + put(c, fGenericOrigin.sons[i], last.sons[i]) + proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int = var t = a assert t.kind == tyObject @@ -363,12 +371,7 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int = t = skipTypes(t, skipPtrs) inc depth if t != nil: - if fGenericOrigin != nil and last.kind == tyGenericInst and - last.len-1 == fGenericOrigin.len: - for i in countup(1, sonsLen(fGenericOrigin) - 1): - let x = PType(idTableGet(c.bindings, fGenericOrigin.sons[i])) - if x == nil: - put(c, fGenericOrigin.sons[i], last.sons[i]) + genericParamPut(c, last, fGenericOrigin) result = depth else: result = -1 @@ -398,7 +401,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = break if r.kind == tyObject and ptrs <= 1: result = r -proc isGenericSubtype(a, f: PType, d: var int): bool = +proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType = nil): bool = assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} var askip = skippedNone var fskip = skippedNone @@ -406,14 +409,17 @@ proc isGenericSubtype(a, f: PType, d: var int): bool = let r = f.skipToObject(fskip) if r == nil: return false var depth = 0 + var last = a # XXX sameObjectType can return false here. Need to investigate # why that is but sameObjectType does way too much work here anyway. while t != nil and r.sym != t.sym and askip == fskip: t = t.sons[0] - if t != nil: t = t.skipToObject(askip) - else: break + if t == nil: break + last = t + t = t.skipToObject(askip) inc depth if t != nil and askip == fskip: + genericParamPut(c, last, fGenericOrigin) d = depth result = true @@ -999,7 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # simply no match for now: discard elif x.kind == tyGenericInst and - ((f.sons[0] == x.sons[0]) or isGenericSubtype(x, f, depth)) and + ((f.sons[0] == x.sons[0]) or isGenericSubType(c, x, f, depth)) and (sonsLen(x) - 1 == sonsLen(f)): for i in countup(1, sonsLen(f) - 1): if x.sons[i].kind == tyGenericParam: @@ -1039,6 +1045,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: put(c, f.sons[i], x) + if result == isNone: + # Here object inheriting from generic/specialized generic object + # crossing path with metatypes/aliases, so we need to separate them + # by checking sym.id + let genericSubtype = isGenericSubType(c, x, f, depth, f) + if not (genericSubtype and aobj.sym.id != fobj.sym.id): + depth = -1 + if depth >= 0: c.inheritancePenalty += depth # bug #4863: We still need to bind generic alias crap, so diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim new file mode 100644 index 0000000000..8c8f90098b --- /dev/null +++ b/tests/generics/tobjecttyperel.nim @@ -0,0 +1,65 @@ +discard """ + output: '''(peel: 0, color: 15) +(color: 15) +17 +(width: 0.0, taste: nil, color: 13) +(width: 0.0, taste: nil, color: 15) +cool''' +""" + +# bug #5241 +type + BaseFruit[T] = object of RootObj + color: T + + MidLevel[T] = object of BaseFruit[T] + + Mango = object of MidLevel[int] + peel: int + + Peach[X, T, Y] = object of T + width: X + taste: Y + +proc setColor[T](self: var BaseFruit[T]) = + self.color = 15 + +proc setColor[T](self: var BaseFruit[T], c: int) = + self.color = c + +var c: Mango +setColor(c) +echo c + +var d: MidLevel[int] +setColor(d) +echo d + +type + FooBase[T] = ref object of RootRef + v: T + BarClient = ref object of FooBase[int] + +proc getColor[T](f: FooBase[T]): T = 17 +var b: BarClient +echo getColor(b) + +var z: Peach[float64, BaseFruit[int], string] +z.setColor(13) +echo z + +z.setColor() +echo z + +# bug #5411 +type + Foo[T] = ref object of RootRef + v: T + Bar = ref object of Foo[int] + +method m(o: RootRef) {.base.} = assert(false, "Abstract method called") +method m[T](o: Foo[T]) = echo "cool" + +var v: Bar +v.new() +v.m() # Abstract method not called anymore \ No newline at end of file From 434a7c842694469c532287319049e35340bb8494 Mon Sep 17 00:00:00 2001 From: Anatoly Galiulin Date: Fri, 24 Mar 2017 05:40:03 +0700 Subject: [PATCH 18/29] Fix posix version of moveFile between different filesystems (#5580) --- lib/pure/os.nim | 66 ++++++++++++++++++++++++++++------------- lib/windows/winlean.nim | 2 ++ tests/stdlib/tio.nim | 1 + 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 39cdf6d81b..a7f7116ef6 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -617,20 +617,6 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", flushFile(d) close(d) -proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadIOEffect, WriteIOEffect].} = - ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. - when defined(Windows): - when useWinUnicode: - let s = newWideCString(source) - let d = newWideCString(dest) - if moveFileW(s, d) == 0'i32: raiseOSError(osLastError()) - else: - if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError()) - else: - if c_rename(source, dest) != 0'i32: - raiseOSError(osLastError(), $strerror(errno)) - when not declared(ENOENT) and not defined(Windows): when NoFakeVars: const ENOENT = cint(2) # 2 on most systems including Solaris @@ -647,25 +633,63 @@ when defined(Windows): template setFileAttributes(file, attrs: untyped): untyped = setFileAttributesA(file, attrs) -proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = - ## Removes the `file`. If this fails, `OSError` is raised. This does not fail +proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = + ## Removes the `file`. If this fails, returns `false`. This does not fail ## if the file never existed in the first place. ## On Windows, ignores the read-only attribute. + result = true when defined(Windows): when useWinUnicode: let f = newWideCString(file) else: let f = file if deleteFile(f) == 0: - if getLastError() == ERROR_ACCESS_DENIED: - if setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) == 0: - raiseOSError(osLastError()) - if deleteFile(f) == 0: - raiseOSError(osLastError()) + result = false + let err = getLastError() + if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND: + result = true + elif err == ERROR_ACCESS_DENIED and + setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and + deleteFile(f) != 0: + result = true else: if c_remove(file) != 0'i32 and errno != ENOENT: + result = false + +proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].} = + ## Removes the `file`. If this fails, `OSError` is raised. This does not fail + ## if the file never existed in the first place. + ## On Windows, ignores the read-only attribute. + if not tryRemoveFile(file): + when defined(Windows): + raiseOSError(osLastError()) + else: raiseOSError(osLastError(), $strerror(errno)) +proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", + tags: [ReadIOEffect, WriteIOEffect].} = + ## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. + when defined(Windows): + when useWinUnicode: + let s = newWideCString(source) + let d = newWideCString(dest) + if moveFileW(s, d) == 0'i32: raiseOSError(osLastError()) + else: + if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError()) + else: + if c_rename(source, dest) != 0'i32: + let err = osLastError() + if err == EXDEV.OSErrorCode: + # Fallback to copy & del + copyFile(source, dest) + try: + removeFile(source) + except: + discard tryRemoveFile(dest) + raise + else: + raiseOSError(err, $strerror(errno)) + proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", tags: [ExecIOEffect].} = ## Executes a `shell command`:idx:. diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 1a251d0cc3..1f8dc9ad6c 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -667,6 +667,8 @@ const # Error Constants const + ERROR_FILE_NOT_FOUND* = 2 + ERROR_PATH_NOT_FOUND* = 3 ERROR_ACCESS_DENIED* = 5 ERROR_HANDLE_EOF* = 38 ERROR_BAD_ARGUMENTS* = 165 diff --git a/tests/stdlib/tio.nim b/tests/stdlib/tio.nim index 93284c1f78..28e1881e8e 100644 --- a/tests/stdlib/tio.nim +++ b/tests/stdlib/tio.nim @@ -35,5 +35,6 @@ except: echo "Second readLine raised an exception" echo line +f.close() removeFile(fn) From d4b4cad4ea14b0a51850d634fe1f1c12c3c04149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Thu, 23 Mar 2017 23:49:07 +0100 Subject: [PATCH 19/29] Added few documentation lines for the unsafeAddr operator. (#5362) --- lib/system.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index b7e2c6ebac..94e10d7dfb 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -154,9 +154,13 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = discard proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = - ## Builtin 'addr' operator for taking the address of a memory location. - ## This works even for ``let`` variables or parameters for better interop - ## with C and so it is considered even more unsafe than the ordinary ``addr``. + ## Builtin 'addr' operator for taking the address of a memory + ## location. This works even for ``let`` variables or parameters + ## for better interop with C and so it is considered even more + ## unsafe than the ordinary ``addr``. When you use it to write a + ## wrapper for a C library, you should always check that the + ## original library does never write to data behind the pointer that + ## is returned from this procedure. ## Cannot be overloaded. discard From 268a1f7cfd9d99ed0bcd2b19f47c35eb7c979e2a Mon Sep 17 00:00:00 2001 From: Dmitriy Fomichev Date: Fri, 24 Mar 2017 02:09:51 +0300 Subject: [PATCH 20/29] Deques compilation error fix (#5591) --- lib/pure/collections/deques.nim | 4 ++-- tests/collections/tdeques.nim | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/collections/tdeques.nim diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index d42679f06c..78953228bc 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -80,7 +80,7 @@ proc `[]`*[T](deq: Deque[T], i: Natural) : T {.inline.} = ## Access the i-th element of `deq` by order from first to last. ## deq[0] is the first, deq[^1] is the last. xBoundsCheck(deq, i) - return deq.data[(deq.first + i) and deq.mask] + return deq.data[(deq.head + i) and deq.mask] proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = ## Access the i-th element of `deq` and returns a mutable @@ -266,4 +266,4 @@ when isMainModule: foo(1,1) foo(2,1) foo(1,5) - foo(3,2) \ No newline at end of file + foo(3,2) diff --git a/tests/collections/tdeques.nim b/tests/collections/tdeques.nim new file mode 100644 index 0000000000..664ce43240 --- /dev/null +++ b/tests/collections/tdeques.nim @@ -0,0 +1,17 @@ +discard """ + output: '''true''' +""" + +import deques + + +proc index(self: Deque[int], idx: Natural): int = + self[idx] + +proc main = + var testDeque = initDeque[int]() + testDeque.addFirst(1) + assert testDeque.index(0) == 1 + +main() +echo "true" From 0d8a503e450b1d240fb5ab914f40aa30d7b6cd8b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 24 Mar 2017 07:35:12 +0100 Subject: [PATCH 21/29] fixes #5597; wrong eager template instantiation in generic context (#5601) --- compiler/semgnrc.nim | 6 +++--- tests/generics/t1050.nim | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 47a094f9d7..3938259ad1 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -51,10 +51,10 @@ template macroToExpand(s): untyped = s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags) template macroToExpandSym(s): untyped = - s.kind in {skMacro, skTemplate} and (s.typ.len == 1) + s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, - ctx: var GenericCtx): PNode = + ctx: var GenericCtx; fromDotExpr=false): PNode = semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) incl(s.flags, sfUsed) case s.kind @@ -145,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.name.id in ctx.toMixin: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: - let syms = semGenericStmtSymbol(c, n, s, ctx) + let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true) if syms.kind == nkSym: let choice = symChoice(c, n, s, scForceOpen) choice.kind = nkClosedSymChoice diff --git a/tests/generics/t1050.nim b/tests/generics/t1050.nim index a6f9a2482d..9e83b5ff06 100644 --- a/tests/generics/t1050.nim +++ b/tests/generics/t1050.nim @@ -14,3 +14,16 @@ proc arrayItem(a: ArrayType): auto = var arr: ArrayType[int] echo arrayItem(arr) +# bug #5597 + +template fail() = "what" + +proc g[T](x: var T) = + x.fail = 3 + +type + Obj = object + fail: int + +var y: Obj +g y From 80ffae62fbaf1a3d2a2320b3fbf4fb665c95a321 Mon Sep 17 00:00:00 2001 From: Konstantin Molchanov Date: Sat, 25 Mar 2017 12:26:34 +0400 Subject: [PATCH 22/29] JS: Times: Add timezone prop to TimeInfo. (#5581) --- lib/pure/times.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 82fc9ad41f..fe35c404c6 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -598,6 +598,7 @@ elif defined(JS): result.year = t.getFullYear() result.weekday = weekDays[t.getDay()] result.yearday = 0 + result.timezone = getTimezone() proc getGMTime(t: Time): TimeInfo = result.second = t.getUTCSeconds() From 21b03257ef62609c3ce29b2c61bebb5cc6b0a243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Sat, 25 Mar 2017 09:28:26 +0100 Subject: [PATCH 23/29] update linenoise (#5357) --- .../linenoise/{clinenoise.c => linenoise.c} | 188 +++++++++++++----- .../linenoise/{clinenoise.h => linenoise.h} | 25 ++- lib/wrappers/linenoise/linenoise.nim | 3 +- 3 files changed, 159 insertions(+), 57 deletions(-) rename lib/wrappers/linenoise/{clinenoise.c => linenoise.c} (86%) rename lib/wrappers/linenoise/{clinenoise.h => linenoise.h} (76%) diff --git a/lib/wrappers/linenoise/clinenoise.c b/lib/wrappers/linenoise/linenoise.c similarity index 86% rename from lib/wrappers/linenoise/clinenoise.c rename to lib/wrappers/linenoise/linenoise.c index c76fc6586a..fce14a7c53 100644 --- a/lib/wrappers/linenoise/clinenoise.c +++ b/lib/wrappers/linenoise/linenoise.c @@ -1,7 +1,5 @@ -/* linenoise.c -- VERSION 1.0 - * - * Guerrilla line editing library against the idea that a line editing lib - * needs to be 20,000 lines of C code. +/* linenoise.c -- guerrilla line editing library against the idea that a + * line editing lib needs to be 20,000 lines of C code. * * You can find the latest source code at: * @@ -12,7 +10,7 @@ * * ------------------------------------------------------------------------ * - * Copyright (c) 2010-2014, Salvatore Sanfilippo + * Copyright (c) 2010-2016, Salvatore Sanfilippo * Copyright (c) 2010-2013, Pieter Noordhuis * * All rights reserved. @@ -113,17 +111,18 @@ #include #include #include +#include #include #include #include -#ifndef __LINENOISE_H -# include "clinenoise.h" -#endif +#include "linenoise.h" #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 4096 static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static linenoiseCompletionCallback *completionCallback = NULL; +static linenoiseHintsCallback *hintsCallback = NULL; +static linenoiseFreeHintsCallback *freeHintsCallback = NULL; static struct termios orig_termios; /* In order to restore at exit.*/ static int rawmode = 0; /* For atexit() function to check if restore is needed*/ @@ -141,7 +140,7 @@ struct linenoiseState { int ofd; /* Terminal stdout file descriptor. */ char *buf; /* Edited line buffer. */ size_t buflen; /* Edited line buffer size. */ - char *prompt; /* Prompt to display. */ + const char *prompt; /* Prompt to display. */ size_t plen; /* Prompt length. */ size_t pos; /* Current cursor position. */ size_t oldpos; /* Previous refresh cursor position. */ @@ -174,7 +173,7 @@ enum KEY_ACTION{ }; static void linenoiseAtExit(void); -int linenoiseHistoryAdd(char *line); +int linenoiseHistoryAdd(const char *line); static void refreshLine(struct linenoiseState *l); /* Debugging macro. */ @@ -411,18 +410,30 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { completionCallback = fn; } +/* Register a hits function to be called to show hits to the user at the + * right of the prompt. */ +void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) { + hintsCallback = fn; +} + +/* Register a function to free the hints returned by the hints callback + * registered with linenoiseSetHintsCallback(). */ +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { + freeHintsCallback = fn; +} + /* This function is used by the callback function registered by the user * in order to add completion options given the input string when the * user typed . See the example.c source code for a very easy to * understand example. */ -void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) { +void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { size_t len = strlen(str); char *copy, **cvec; - copy = (char*)malloc(len+1); + copy = malloc(len+1); if (copy == NULL) return; memcpy(copy,str,len+1); - cvec = (char**)realloc(lc->cvec,sizeof(char*)*(lc->len+1)); + cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); if (cvec == NULL) { free(copy); return; @@ -447,12 +458,12 @@ static void abInit(struct abuf *ab) { ab->len = 0; } -static void abAppend(struct abuf *ab, char *s, int len) { - char *neww = (char*)realloc(ab->b,ab->len+len); +static void abAppend(struct abuf *ab, const char *s, int len) { + char *new = realloc(ab->b,ab->len+len); - if (neww == NULL) return; - memcpy(neww+ab->len,s,len); - ab->b = neww; + if (new == NULL) return; + memcpy(new+ab->len,s,len); + ab->b = new; ab->len += len; } @@ -460,6 +471,30 @@ static void abFree(struct abuf *ab) { free(ab->b); } +/* Helper of refreshSingleLine() and refreshMultiLine() to show hints + * to the right of the prompt. */ +void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) { + char seq[64]; + if (hintsCallback && plen+l->len < l->cols) { + int color = -1, bold = 0; + char *hint = hintsCallback(l->buf,&color,&bold); + if (hint) { + int hintlen = strlen(hint); + int hintmaxlen = l->cols-(plen+l->len); + if (hintlen > hintmaxlen) hintlen = hintmaxlen; + if (bold == 1 && color == -1) color = 37; + if (color != -1 || bold != 0) + snprintf(seq,64,"\033[%d;%d;49m",bold,color); + abAppend(ab,seq,strlen(seq)); + abAppend(ab,hint,hintlen); + if (color != -1 || bold != 0) + abAppend(ab,"\033[0m",4); + /* Call the function to free the hint returned. */ + if (freeHintsCallback) freeHintsCallback(hint); + } + } +} + /* Single line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, @@ -489,6 +524,8 @@ static void refreshSingleLine(struct linenoiseState *l) { /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,buf,len); + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); /* Erase to right */ snprintf(seq,64,"\x1b[0K"); abAppend(&ab,seq,strlen(seq)); @@ -542,6 +579,9 @@ static void refreshMultiLine(struct linenoiseState *l) { abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->buf,l->len); + /* Show hits if any. */ + refreshShowHints(&ab,l,plen); + /* If we are at the very end of the screen with our prompt, we need to * emit a newline and move the prompt to the first column. */ if (l->pos && @@ -602,7 +642,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) { l->pos++; l->len++; l->buf[l->len] = '\0'; - if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) { + if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) { /* Avoid a full update of the line in the * trivial case. */ if (write(l->ofd,&c,1) == -1) return -1; @@ -725,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { * when ctrl+d is typed. * * The function returns the length of the current buffer. */ -static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, char *prompt) +static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { struct linenoiseState l; @@ -776,6 +816,14 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, history_len--; free(history[history_len]); if (mlmode) linenoiseEditMoveEnd(&l); + if (hintsCallback) { + /* Force a refresh without hints to leave the previous + * line as the user typed it after a newline. */ + linenoiseHintsCallback *hc = hintsCallback; + hintsCallback = NULL; + refreshLine(&l); + hintsCallback = hc; + } return (int)l.len; case CTRL_C: /* ctrl-c */ errno = EAGAIN; @@ -931,41 +979,71 @@ void linenoisePrintKeyCodes(void) { /* This function calls the line editing function linenoiseEdit() using * the STDIN file descriptor set in raw mode. */ -static int linenoiseRaw(char *buf, size_t buflen, char *prompt) { +static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { int count; if (buflen == 0) { errno = EINVAL; return -1; } - if (!isatty(STDIN_FILENO)) { - /* Not a tty: read from file / pipe. */ - if (fgets(buf, buflen, stdin) == NULL) return -1; - count = strlen(buf); - if (count && buf[count-1] == '\n') { - count--; - buf[count] = '\0'; - } - } else { - /* Interactive editing. */ - if (enableRawMode(STDIN_FILENO) == -1) return -1; - count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); - disableRawMode(STDIN_FILENO); - printf("\n"); - } + + if (enableRawMode(STDIN_FILENO) == -1) return -1; + count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); + disableRawMode(STDIN_FILENO); + printf("\n"); return count; } +/* This function is called when linenoise() is called with the standard + * input file descriptor not attached to a TTY. So for example when the + * program using linenoise is called in pipe or with a file redirected + * to its standard input. In this case, we want to be able to return the + * line regardless of its length (by default we are limited to 4k). */ +static char *linenoiseNoTTY(void) { + char *line = NULL; + size_t len = 0, maxlen = 0; + + while(1) { + if (len == maxlen) { + if (maxlen == 0) maxlen = 16; + maxlen *= 2; + char *oldval = line; + line = realloc(line,maxlen); + if (line == NULL) { + if (oldval) free(oldval); + return NULL; + } + } + int c = fgetc(stdin); + if (c == EOF || c == '\n') { + if (c == EOF && len == 0) { + free(line); + return NULL; + } else { + line[len] = '\0'; + return line; + } + } else { + line[len] = c; + len++; + } + } +} + /* The high level function that is the main API of the linenoise library. * This function checks if the terminal has basic capabilities, just checking * for a blacklist of stupid terminals, and later either calls the line * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ -char *linenoise(char *prompt) { +char *linenoise(const char *prompt) { char buf[LINENOISE_MAX_LINE]; int count; - if (isUnsupportedTerm()) { + if (!isatty(STDIN_FILENO)) { + /* Not a tty: read from file / pipe. In this mode we don't want any + * limit to the line size, so we call a function to handle that. */ + return linenoiseNoTTY(); + } else if (isUnsupportedTerm()) { size_t len; printf("%s",prompt); @@ -984,6 +1062,14 @@ char *linenoise(char *prompt) { } } +/* This is just a wrapper the user may want to call in order to make sure + * the linenoise returned buffer is freed with the same allocator it was + * created with. Useful when the main program is using an alternative + * allocator. */ +void linenoiseFree(void *ptr) { + free(ptr); +} + /* ================================ History ================================= */ /* Free the history, but does not reset it. Only used when we have to @@ -1011,14 +1097,14 @@ static void linenoiseAtExit(void) { * histories, but will work well for a few hundred of entries. * * Using a circular buffer is smarter, but a bit more complex to handle. */ -int linenoiseHistoryAdd(char *line) { +int linenoiseHistoryAdd(const char *line) { char *linecopy; if (history_max_len == 0) return 0; /* Initialization on first call. */ if (history == NULL) { - history = (char**)malloc(sizeof(char*)*history_max_len); + history = malloc(sizeof(char*)*history_max_len); if (history == NULL) return 0; memset(history,0,(sizeof(char*)*history_max_len)); } @@ -1045,14 +1131,14 @@ int linenoiseHistoryAdd(char *line) { * just the latest 'len' elements if the new history length value is smaller * than the amount of items already inside the history. */ int linenoiseHistorySetMaxLen(int len) { - char **neww; + char **new; if (len < 1) return 0; if (history) { int tocopy = history_len; - neww = (char**)malloc(sizeof(char*)*len); - if (neww == NULL) return 0; + new = malloc(sizeof(char*)*len); + if (new == NULL) return 0; /* If we can't copy everything, free the elements we'll not use. */ if (len < tocopy) { @@ -1061,10 +1147,10 @@ int linenoiseHistorySetMaxLen(int len) { for (j = 0; j < tocopy-len; j++) free(history[j]); tocopy = len; } - memset(neww,0,sizeof(char*)*len); - memcpy(neww,history+(history_len-tocopy), sizeof(char*)*tocopy); + memset(new,0,sizeof(char*)*len); + memcpy(new,history+(history_len-tocopy), sizeof(char*)*tocopy); free(history); - history = neww; + history = new; } history_max_len = len; if (history_len > history_max_len) @@ -1074,11 +1160,15 @@ int linenoiseHistorySetMaxLen(int len) { /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ -int linenoiseHistorySave(char *filename) { - FILE *fp = fopen(filename,"w"); +int linenoiseHistorySave(const char *filename) { + mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + FILE *fp; int j; + fp = fopen(filename,"w"); + umask(old_umask); if (fp == NULL) return -1; + chmod(filename,S_IRUSR|S_IWUSR); for (j = 0; j < history_len; j++) fprintf(fp,"%s\n",history[j]); fclose(fp); @@ -1090,7 +1180,7 @@ int linenoiseHistorySave(char *filename) { * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ -int linenoiseHistoryLoad(char *filename) { +int linenoiseHistoryLoad(const char *filename) { FILE *fp = fopen(filename,"r"); char buf[LINENOISE_MAX_LINE]; diff --git a/lib/wrappers/linenoise/clinenoise.h b/lib/wrappers/linenoise/linenoise.h similarity index 76% rename from lib/wrappers/linenoise/clinenoise.h rename to lib/wrappers/linenoise/linenoise.h index a15845f863..ed20232c57 100644 --- a/lib/wrappers/linenoise/clinenoise.h +++ b/lib/wrappers/linenoise/linenoise.h @@ -39,22 +39,35 @@ #ifndef __LINENOISE_H #define __LINENOISE_H +#ifdef __cplusplus +extern "C" { +#endif + typedef struct linenoiseCompletions { size_t len; char **cvec; } linenoiseCompletions; -typedef void(linenoiseCompletionCallback)(char *, linenoiseCompletions *); +typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); +typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); +typedef void(linenoiseFreeHintsCallback)(void *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); -void linenoiseAddCompletion(linenoiseCompletions *, char *); +void linenoiseSetHintsCallback(linenoiseHintsCallback *); +void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); +void linenoiseAddCompletion(linenoiseCompletions *, const char *); -char *linenoise(char *prompt); -int linenoiseHistoryAdd(char *line); +char *linenoise(const char *prompt); +void linenoiseFree(void *ptr); +int linenoiseHistoryAdd(const char *line); int linenoiseHistorySetMaxLen(int len); -int linenoiseHistorySave(char *filename); -int linenoiseHistoryLoad(char *filename); +int linenoiseHistorySave(const char *filename); +int linenoiseHistoryLoad(const char *filename); void linenoiseClearScreen(void); void linenoiseSetMultiLine(int ml); void linenoisePrintKeyCodes(void); +#ifdef __cplusplus +} +#endif + #endif /* __LINENOISE_H */ diff --git a/lib/wrappers/linenoise/linenoise.nim b/lib/wrappers/linenoise/linenoise.nim index 4ac7bf4a8c..a6260eb128 100644 --- a/lib/wrappers/linenoise/linenoise.nim +++ b/lib/wrappers/linenoise/linenoise.nim @@ -14,8 +14,7 @@ type CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.} -{.emit: staticRead"clinenoise.h".} -{.emit: staticRead"clinenoise.c".} +{.compile: "linenoise.c".} proc setCompletionCallback*(a2: ptr CompletionCallback) {. importc: "linenoiseSetCompletionCallback".} From 1268ca79e5436e33a9fca0999477adf8f3e937ae Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 26 Mar 2017 09:30:59 +0200 Subject: [PATCH 24/29] fixes #5599 (#5610) --- compiler/cgmeth.nim | 10 ++++++++-- lib/pure/segfaults.nim | 4 ---- lib/system.nim | 8 ++++++-- lib/system/chcks.nim | 6 +++++- lib/system/jssys.nim | 4 ++++ web/news/e031_version_0_16_2.rst | 3 +++ 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index d05e395b97..e14306e568 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -229,6 +229,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = var base = lastSon(methods[0].ast).sym result = base var paramLen = sonsLen(base.typ) + var nilchecks = newNodeI(nkStmtList, base.info) var disp = newNodeI(nkIfStmt, base.info) var ands = getSysSym("and") var iss = getSysSym("of") @@ -239,7 +240,11 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = if contains(relevantCols, col): var isn = newNodeIT(nkCall, base.info, getSysType(tyBool)) addSon(isn, newSymNode(iss)) - addSon(isn, newSymNode(base.typ.n.sons[col].sym)) + let param = base.typ.n.sons[col].sym + addSon(isn, newSymNode(param)) + if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: + addSon(nilchecks, newTree(nkCall, + newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param))) addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col])) if cond != nil: var a = newNodeIT(nkCall, base.info, getSysType(tyBool)) @@ -270,7 +275,8 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = addSon(disp, a) else: disp = ret - result.ast.sons[bodyPos] = disp + nilchecks.add disp + result.ast.sons[bodyPos] = nilchecks proc generateMethodDispatchers*(g: ModuleGraph): PNode = result = newNode(nkStmtList) diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim index 3823d037c3..3735084302 100644 --- a/lib/pure/segfaults.nim +++ b/lib/pure/segfaults.nim @@ -13,10 +13,6 @@ ## ## Tested on these OSes: Linux, Windows, OSX -type - NilAccessError* = object of SystemError ## \ - ## Raised on dereferences of ``nil`` pointers. - # do allocate memory upfront: var se: ref NilAccessError new(se) diff --git a/lib/system.nim b/lib/system.nim index 94e10d7dfb..371cf8544d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -558,6 +558,10 @@ type ## Raised if it is attempted to send a message to a dead thread. ## ## See the full `exception hierarchy `_. + NilAccessError* = object of SystemError ## \ + ## Raised on dereferences of ``nil`` pointers. + ## + ## This is only raised if the ``segfaults.nim`` module was imported! {.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, @@ -2467,8 +2471,8 @@ template accumulateResult*(iter: untyped) = const NimStackTrace = compileOption("stacktrace") template coroutinesSupportedPlatform(): bool = - when defined(sparc) or defined(ELATE) or compileOption("gc", "v2") or - defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcStack) or + when defined(sparc) or defined(ELATE) or compileOption("gc", "v2") or + defined(boehmgc) or defined(gogc) or defined(nogc) or defined(gcStack) or defined(gcMarkAndSweep): false else: diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 27a3708ea7..1520f231e1 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -50,7 +50,11 @@ proc chckRangeF(x, a, b: float): float = proc chckNil(p: pointer) = if p == nil: - sysFatal(ValueError, "attempt to write to a nil address") + sysFatal(NilAccessError, "attempt to write to a nil address") + +proc chckNilDisp(p: pointer) {.compilerproc.} = + if p == nil: + sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") proc chckObj(obj, subclass: PNimType) {.compilerproc.} = # checks if obj is of type subclass: diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 39ce1183b3..9ef4a02f2a 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -642,6 +642,10 @@ proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} = proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b +proc chckNilDisp(p: pointer) {.compilerproc.} = + if p == nil: + sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") + type NimString = string # hack for hti.nim include "system/hti" diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 35f3716a30..8517dacb0d 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -42,6 +42,9 @@ Changes affecting backwards compatibility AST that is the same as what is used to define an enum. Previously the AST returned had a repeated ``EnumTy`` node and was missing the initial pragma node (which is currently empty for an enum). +- If the dispatcher parameter's value used in multi method is ``nil``, + a ``NilError`` exception is raised. The old behavior was that the method + would be a ``nop`` then. Library Additions From 481d8ba24afaffdc926795a42150e5cd1e1c69b3 Mon Sep 17 00:00:00 2001 From: pgkos Date: Sun, 26 Mar 2017 09:42:15 +0200 Subject: [PATCH 25/29] Fix segfault in db_mysql fastRows (#5605) --- lib/impure/db_mysql.nim | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 1b7f1de617..1b79b35434 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -173,17 +173,33 @@ iterator fastRows*(db: DbConn, query: SqlQuery, rawExec(db, query, args) var sqlres = mysql.useResult(db) if sqlres != nil: - var L = int(mysql.numFields(sqlres)) - var result = newRow(L) - var row: cstringArray + var + L = int(mysql.numFields(sqlres)) + row: cstringArray + result: Row + backup: Row + newSeq(result, L) while true: row = mysql.fetchRow(sqlres) if row == nil: break for i in 0..L-1: - setLen(result[i], 0) if row[i] == nil: + if backup == nil: + newSeq(backup, L) + if backup[i] == nil and result[i] != nil: + shallowCopy(backup[i], result[i]) result[i] = nil else: + if result[i] == nil: + if backup != nil: + if backup[i] == nil: + backup[i] = "" + shallowCopy(result[i], backup[i]) + setLen(result[i], 0) + else: + result[i] = "" + else: + setLen(result[i], 0) add(result[i], row[i]) yield result properFreeResult(sqlres, row) From d02486aa48e51b8db51baa762d6e87fe7eb91f04 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 26 Mar 2017 20:24:06 +0200 Subject: [PATCH 26/29] compiler: better error messages (#5613) --- compiler/lookups.nim | 33 +++++++++++++++++------------ compiler/semexprs.nim | 14 ++++++------ compiler/semstmts.nim | 7 +++--- tests/discard/tneedsdiscard.nim | 4 ++-- tests/discard/tvoidcontext.nim | 2 +- tests/errmsgs/tdont_show_system.nim | 2 +- tests/exprs/texprstmt.nim | 2 +- tests/exprs/tstmtexp.nim | 2 +- 8 files changed, 37 insertions(+), 29 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 089e69ff99..5c326d10a1 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -15,17 +15,28 @@ import proc ensureNoMissingOrUnusedSymbols(scope: PScope) -proc considerQuotedIdent*(n: PNode): PIdent = +proc noidentError(n, origin: PNode) = + var m = "" + if origin != nil: + m.add "in expression '" & origin.renderTree & "': " + m.add "identifier expected, but found '" & n.renderTree & "'" + localError(n.info, m) + +proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent = ## Retrieve a PIdent from a PNode, taking into account accent nodes. + ## ``origin`` can be nil. If it is not nil, it is used for a better + ## error message. + template handleError(n, origin: PNode) = + noidentError(n, origin) + result = getIdent"" + case n.kind of nkIdent: result = n.ident of nkSym: result = n.sym.name of nkAccQuoted: case n.len - of 0: - localError(n.info, errIdentifierExpected, renderTree(n)) - result = getIdent"" - of 1: result = considerQuotedIdent(n.sons[0]) + of 0: handleError(n, origin) + of 1: result = considerQuotedIdent(n.sons[0], origin) else: var id = "" for i in 0.. " + else: handleError(n, origin) result = getIdent(id) of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name else: - localError(n.info, errIdentifierExpected, renderTree(n)) - result = getIdent"" + handleError(n, origin) template addSym*(scope: PScope, s: PSym) = strTableAdd(scope.symbols, s) @@ -353,7 +361,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = if n.sons[1].kind == nkIdent: ident = n.sons[1].ident elif n.sons[1].kind == nkAccQuoted: - ident = considerQuotedIdent(n.sons[1]) + ident = considerQuotedIdent(n.sons[1], n) if ident != nil: if o.m == c.module: # a module may access its private members: @@ -363,8 +371,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = else: result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n) else: - localError(n.sons[1].info, errIdentifierExpected, - renderTree(n.sons[1])) + noidentError(n.sons[1], n) result = errorSym(c, n.sons[1]) of nkClosedSymChoice, nkOpenSymChoice: o.mode = oimSymChoice diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4d698dbfc9..80b8440536 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -355,7 +355,7 @@ proc semOpAux(c: PContext, n: PNode) = var a = n.sons[i] if a.kind == nkExprEqExpr and sonsLen(a) == 2: var info = a.sons[0].info - a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0]), info) + a.sons[0] = newIdentNode(considerQuotedIdent(a.sons[0], a), info) a.sons[1] = semExprWithType(c, a.sons[1], flags) a.typ = a.sons[1].typ else: @@ -1076,7 +1076,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType}) #restoreOldStyleType(n.sons[0]) - var i = considerQuotedIdent(n.sons[1]) + var i = considerQuotedIdent(n.sons[1], n) var ty = n.sons[0].typ var f: PSym = nil result = nil @@ -1160,7 +1160,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode = addSon(result, n.sons[1]) addSon(result, copyTree(n[0])) else: - var i = considerQuotedIdent(n.sons[1]) + var i = considerQuotedIdent(n.sons[1], n) result = newNodeI(nkDotCall, n.info) result.flags.incl nfDotField addSon(result, newIdentNode(i, n[1].info)) @@ -1280,7 +1280,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = c.p.bracketExpr = oldBracketExpr proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = - var id = considerQuotedIdent(a[1]) + var id = considerQuotedIdent(a[1], a) var setterId = newIdentNode(getIdent(id.s & '='), n.info) # a[0] is already checked for semantics, that does ``builtinFieldAccess`` # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for @@ -1529,7 +1529,7 @@ proc lookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = checkSonsLen(n, 2) var m = lookUpForDefined(c, n.sons[0], onlyCurrentScope) if m != nil and m.kind == skModule: - let ident = considerQuotedIdent(n[1]) + let ident = considerQuotedIdent(n[1], n) if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) else: @@ -1548,7 +1548,7 @@ proc semDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PNode = checkSonsLen(n, 2) # we replace this node by a 'true' or 'false' node: result = newIntNode(nkIntLit, 0) - if not onlyCurrentScope and considerQuotedIdent(n[0]).s == "defined": + if not onlyCurrentScope and considerQuotedIdent(n[0], n).s == "defined": if n.sons[1].kind != nkIdent: localError(n.info, "obsolete usage of 'defined', use 'declared' instead") elif condsyms.isDefined(n.sons[1].ident): @@ -2097,7 +2097,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if it.kind != nkExprColonExpr: localError(n.info, errNamedExprExpected) break - let id = considerQuotedIdent(it.sons[0]) + let id = considerQuotedIdent(it.sons[0], it) if containsOrIncl(ids, id.id): localError(it.info, errFieldInitTwice, id.s) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 9a1850932d..f4efcc0ff5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -160,10 +160,11 @@ proc discardCheck(c: PContext, result: PNode) = else: var n = result while n.kind in skipForDiscardable: n = n.lastSon + var s = "expression '" & $n & "' is of type '" & + result.typ.typeToString & "' and has to be discarded" if result.typ.kind == tyProc: - localError(n.info, "value of type '" & result.typ.typeToString & "' has to be discarded; for a function call use ()") - else: - localError(n.info, errDiscardValueX, result.typ.typeToString) + s.add "; for a function call use ()" + localError(n.info, s) proc semIf(c: PContext, n: PNode): PNode = result = n diff --git a/tests/discard/tneedsdiscard.nim b/tests/discard/tneedsdiscard.nim index 2df3531e73..509e8233b3 100644 --- a/tests/discard/tneedsdiscard.nim +++ b/tests/discard/tneedsdiscard.nim @@ -1,10 +1,10 @@ discard """ line: 10 - errormsg: "value of type 'bool' has to be discarded" + errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded''' """ proc p = - var f: TFile + var f: File echo "hi" open(f, "arg.txt") diff --git a/tests/discard/tvoidcontext.nim b/tests/discard/tvoidcontext.nim index c3ea68baec..25e0d4fdff 100644 --- a/tests/discard/tvoidcontext.nim +++ b/tests/discard/tvoidcontext.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "value of type 'string' has to be discarded" + errormsg: '''expression '"invalid"' is of type 'string' and has to be discarded''' line: 12 """ diff --git a/tests/errmsgs/tdont_show_system.nim b/tests/errmsgs/tdont_show_system.nim index 6963a8a3fe..830113218e 100644 --- a/tests/errmsgs/tdont_show_system.nim +++ b/tests/errmsgs/tdont_show_system.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "value of type 'bool' has to be discarded" + errormsg: "expression 'true' is of type 'bool' and has to be discarded" line: 13 file: "tdont_show_system.nim" """ diff --git a/tests/exprs/texprstmt.nim b/tests/exprs/texprstmt.nim index 79323d82a7..0e92702e89 100644 --- a/tests/exprs/texprstmt.nim +++ b/tests/exprs/texprstmt.nim @@ -1,6 +1,6 @@ discard """ line: 10 - errormsg: "value of type 'string' has to be discarded" + errormsg: "expression 'result[1 .. -(len(result), 1)]' is of type 'string' and has to be discarded" """ # bug #578 diff --git a/tests/exprs/tstmtexp.nim b/tests/exprs/tstmtexp.nim index fe60dd3ba6..0fb835bc6e 100644 --- a/tests/exprs/tstmtexp.nim +++ b/tests/exprs/tstmtexp.nim @@ -1,7 +1,7 @@ discard """ file: "tstmtexp.nim" line: 8 - errormsg: "value of type 'int literal(5)' has to be discarded" + errormsg: "expression '5' is of type 'int literal(5)' and has to be discarded" """ # Test 3 From bef86f55ceefb1f2ea3fb95a1a5112530707bd46 Mon Sep 17 00:00:00 2001 From: Konstantin Molchanov Date: Mon, 27 Mar 2017 00:14:48 +0400 Subject: [PATCH 27/29] Times: JS: Add yearday to TimeInfo. Add yearday calculation to getLocalTime and getGMTime, so that yearday is not 0 for TimeInfo instances under JS backend. Yearday 0 has no sense and contradicts the behaviour under C backend, where yearday is an int from 1 to 365, i.e. cannot be 0 even theoretically. --- lib/pure/times.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index fe35c404c6..2b7c221452 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -597,9 +597,12 @@ elif defined(JS): result.month = Month(t.getMonth()) result.year = t.getFullYear() result.weekday = weekDays[t.getDay()] - result.yearday = 0 result.timezone = getTimezone() + result.yearday = result.monthday - 1 + for month in mJan.. Date: Mon, 27 Mar 2017 21:14:02 +0400 Subject: [PATCH 28/29] Tests: Times: JS: Add test for yearday attribute. --- tests/js/ttimes.nim | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/js/ttimes.nim diff --git a/tests/js/ttimes.nim b/tests/js/ttimes.nim new file mode 100644 index 0000000000..a39ebe0b3b --- /dev/null +++ b/tests/js/ttimes.nim @@ -0,0 +1,13 @@ +# test times module with js +discard """ + action: run +""" + +import times + +# $ date --date='@2147483647' +# Tue 19 Jan 03:14:07 GMT 2038 + +block yeardayTest: + # check if yearday attribute is properly set on TimeInfo creation + doAssert fromSeconds(2147483647).getGMTime().yearday == 0 From 41e83f7a34f11ea09accaa7d8667ccbabeb0eccf Mon Sep 17 00:00:00 2001 From: Konstantin Molchanov Date: Mon, 27 Mar 2017 21:28:31 +0400 Subject: [PATCH 29/29] Tests: Times: JS: Fix test. --- tests/js/ttimes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/js/ttimes.nim b/tests/js/ttimes.nim index a39ebe0b3b..644e9670af 100644 --- a/tests/js/ttimes.nim +++ b/tests/js/ttimes.nim @@ -10,4 +10,4 @@ import times block yeardayTest: # check if yearday attribute is properly set on TimeInfo creation - doAssert fromSeconds(2147483647).getGMTime().yearday == 0 + doAssert fromSeconds(2147483647).getGMTime().yearday == 18