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/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") 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/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) 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/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, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 32f74e0509..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 @@ -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 @@ -1530,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: @@ -1549,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): @@ -2084,9 +2083,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: @@ -2099,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) @@ -2125,6 +2123,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: 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/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 .. = 0: c.inheritancePenalty += depth # bug #4863: We still need to bind generic alias crap, so 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..`_. + 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, @@ -2463,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/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/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/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/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/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".} 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: 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" 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 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 diff --git a/tests/generics/tforward_generic.nim b/tests/generics/tforward_generic.nim index 169279cb3d..f43e7455fc 100644 --- a/tests/generics/tforward_generic.nim +++ b/tests/generics/tforward_generic.nim @@ -1,5 +1,6 @@ discard """ output: '''b() +720 120.0 720 120.0''' """ @@ -16,13 +17,12 @@ proc fac[T](x: T): T = echo fac(6), " ", fac(5.0) -when false: - # This still doesn't work... - # test recursive generic with forwarding: - proc fac2[T](x: T): T +# test recursive generic with forwarding: +proc fac2[T](x: T): T - echo fac2(6), " ", fac2(5.0) +echo fac2(6), " ", fac2(5.0) + +proc fac2[T](x: T): T = + if x == 0: return 1 + else: return fac2(x-1)*x - proc fac2[T](x: T): T = - if x == 0: return 1 - else: return fac2(x-1)*x diff --git a/tests/generics/tforwardgeneric.nim b/tests/generics/tforwardgeneric.nim index af0c7daf41..9f68bf332e 100644 --- a/tests/generics/tforwardgeneric.nim +++ b/tests/generics/tforwardgeneric.nim @@ -1,7 +1,6 @@ discard """ - output: "1.1000000000000001e+00 11" + output: "1.1 11\n42\n0" ccodecheck: "!@'ClEnv'" - disabled: "true" """ proc p[T](a, b: T): T @@ -12,3 +11,18 @@ proc p[T](a, b: T): T = let c = b result = a + b + c +# https://github.com/nim-lang/Nim/issues/4908 +proc foo(t: typedesc[int]): int +proc bar(): int = foo(int) +proc foo(t: typedesc[int]): int = + return 0 + +# https://github.com/nim-lang/Nim/issues/4104 +proc print[T](t: T) # Error: implementation of 'print.print(t: int)' expected +print 42 # moving this line after the implementation fixes the error, + # but such behaviour makes forward declaration pointless +proc print[T](t: T) = + echo t + +echo bar() + 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 diff --git a/tests/js/ttimes.nim b/tests/js/ttimes.nim new file mode 100644 index 0000000000..644e9670af --- /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 == 18 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) 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" 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/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..