diff options
author | Araq <rumpf_a@web.de> | 2011-06-26 20:17:19 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2011-06-26 20:17:19 +0200 |
commit | e25384db8907f846f3c053379cf5b431c4d28760 (patch) | |
tree | 73f9fcb73ff0b849805077e17889111314d1bf56 | |
parent | c9d21164beef5850b8c3ebb4faf70b59139644d4 (diff) | |
download | Nim-e25384db8907f846f3c053379cf5b431c4d28760.tar.gz |
improvements to get code size down for programs that don't use GC
-rwxr-xr-x | compiler/ccgexprs.nim | 3 | ||||
-rwxr-xr-x | install.sh | 4 | ||||
-rwxr-xr-x | lib/system.nim | 59 | ||||
-rwxr-xr-x | lib/system/debugger.nim | 39 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 186 | ||||
-rwxr-xr-x | lib/system/gc.nim | 1 | ||||
-rwxr-xr-x | lib/system/mmdisp.nim | 13 | ||||
-rwxr-xr-x | lib/system/sysstr.nim | 31 | ||||
-rwxr-xr-x | lib/system/threads.nim | 5 | ||||
-rw-r--r-- | tests/accept/compile/tcodegenbug1.nim | 55 | ||||
-rw-r--r-- | tests/accept/compile/tnimrodnode_for_runtime.nim | 13 | ||||
-rw-r--r-- | tests/accept/run/tgenericassigntuples.nim | 16 | ||||
-rw-r--r-- | tests/accept/run/tsimplesort.nim | 346 | ||||
-rw-r--r-- | tests/reject/temptycaseobj.nim | 14 | ||||
-rw-r--r-- | tests/reject/tprocvar.nim | 18 | ||||
-rwxr-xr-x | tests/stckovfl.nim | 4 | ||||
-rwxr-xr-x | todo.txt | 16 | ||||
-rwxr-xr-x | web/news.txt | 2 |
18 files changed, 643 insertions, 182 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index d054e5bac..33c654982 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1451,6 +1451,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = mInSet: genSetOp(p, e, d, op) of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mCreateThread: + var opr = e.sons[0].sym + if lfNoDecl notin opr.loc.flags: + discard cgsym(p.module, opr.loc.r.ropeToStr) genCall(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e) diff --git a/install.sh b/install.sh index 4ac8d5215..a7a87de14 100755 --- a/install.sh +++ b/install.sh @@ -203,8 +203,6 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/system/threads.nim cp lib/core/macros.nim $libdir/core/macros.nim || exit 1 chmod 644 $libdir/core/macros.nim - cp lib/core/marshal.nim $libdir/core/marshal.nim || exit 1 - chmod 644 $libdir/core/marshal.nim cp lib/core/typeinfo.nim $libdir/core/typeinfo.nim || exit 1 chmod 644 $libdir/core/typeinfo.nim cp lib/pure/algorithm.nim $libdir/pure/algorithm.nim || exit 1 @@ -241,6 +239,8 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/pure/lexbase.nim cp lib/pure/logging.nim $libdir/pure/logging.nim || exit 1 chmod 644 $libdir/pure/logging.nim + cp lib/pure/marshal.nim $libdir/pure/marshal.nim || exit 1 + chmod 644 $libdir/pure/marshal.nim cp lib/pure/math.nim $libdir/pure/math.nim || exit 1 chmod 644 $libdir/pure/math.nim cp lib/pure/md5.nim $libdir/pure/md5.nim || exit 1 diff --git a/lib/system.nim b/lib/system.nim index e19a9749d..5ece9375e 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1387,7 +1387,7 @@ const nimrodStackTrace = compileOption("stacktrace") # of the code var - dbgLineHook*: proc = nil + dbgLineHook*: proc ## set this variable to provide a procedure that should be called before ## each executed instruction. This should only be used by debuggers! ## Only code compiled with the ``debugger:on`` switch calls this hook. @@ -1397,6 +1397,25 @@ var ## application code should never set this hook! You better know what you ## do when setting this. If ``raiseHook`` returns false, the exception ## is caught and does not propagate further through the call stack. + + outOfMemHook*: proc + ## set this variable to provide a procedure that should be called + ## in case of an `out of memory`:idx: event. The standard handler + ## writes an error message and terminates the program. `outOfMemHook` can + ## be used to raise an exception in case of OOM like so: + ## + ## code-block:: nimrod + ## var gOutOfMem: ref EOutOfMemory + ## new(gOutOfMem) # need to be allocated *before* OOM really happened! + ## gOutOfMem.msg = "out of memory" + ## + ## proc handleOOM() = + ## raise gOutOfMem + ## + ## system.outOfMemHook = handleOOM + ## + ## If the handler does not raise an exception, ordinary control flow + ## continues and the program is terminated. type PFrame = ptr TFrame @@ -1710,25 +1729,6 @@ when not defined(EcmaScript) and not defined(NimrodVM): # as it would recurse endlessly! include "system/arithm" {.pop.} # stack trace - - include "system/sysio" - - iterator lines*(filename: string): string = - ## Iterate over any line in the file named `filename`. - ## If the file does not exist `EIO` is raised. - var f = open(filename) - var res = "" - while not endOfFile(f): - rawReadLine(f, res) - yield res - Close(f) - - iterator lines*(f: TFile): string = - ## Iterate over any line in the file `f`. - var res = "" - while not endOfFile(f): - rawReadLine(f, res) - yield res include "system/dyncalls" include "system/sets" @@ -1763,6 +1763,25 @@ when not defined(EcmaScript) and not defined(NimrodVM): include "system/sysstr" {.pop.} + include "system/sysio" + + iterator lines*(filename: string): string = + ## Iterate over any line in the file named `filename`. + ## If the file does not exist `EIO` is raised. + var f = open(filename) + var res = "" + while not endOfFile(f): + rawReadLine(f, res) + yield res + Close(f) + + iterator lines*(f: TFile): string = + ## Iterate over any line in the file `f`. + var res = "" + while not endOfFile(f): + rawReadLine(f, res) + yield res + include "system/assign" include "system/repr" diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim index 2dccd8579..8f381585b 100755 --- a/lib/system/debugger.nim +++ b/lib/system/debugger.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2009 Andreas Rumpf +# (c) Copyright 2011 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -138,10 +138,8 @@ proc dbgShowCurrentProc(dbgFramePointer: PFrame) = write(stdout, "*** endb| (procedure name not available) ***\n") proc dbgShowExecutionPoint() = - ThreadGlobals() - write(stdout, "*** endb| " & $(||framePtr).filename & - "(" & $(||framePtr).line & ") " & - $(||framePtr).procname & " ***\n") + write(stdout, "*** endb| " & $framePtr.filename & + "(" & $framePtr.line & ") " & $framePtr.procname & " ***\n") when defined(windows) or defined(dos) or defined(os2): {.define: FileSystemCaseInsensitive.} @@ -172,10 +170,9 @@ proc fileMatches(c, bp: cstring): bool = return true proc dbgBreakpointReached(line: int): int = - ThreadGlobals() for i in 0..dbgBPlen-1: if line >= dbgBP[i].low and line <= dbgBP[i].high and - fileMatches((||framePtr).filename, dbgBP[i].filename): return i + fileMatches(framePtr.filename, dbgBP[i].filename): return i return -1 proc scanAndAppendWord(src: string, a: var string, start: int): int = @@ -257,7 +254,6 @@ proc hasExt(s: string): bool = return false proc setBreakPoint(s: string, start: int) = - ThreadGlobals() var dbgTemp: string var i = scanWord(s, dbgTemp, start) if i <= start: @@ -272,7 +268,7 @@ proc setBreakPoint(s: string, start: int) = i = scanNumber(s, dbgBP[x].low, i) if dbgBP[x].low == 0: # set to current line: - dbgBP[x].low = (||framePtr).line + dbgBP[x].low = framePtr.line i = scanNumber(s, dbgBP[x].high, i) if dbgBP[x].high == 0: # set to low: dbgBP[x].high = dbgBP[x].low @@ -281,7 +277,7 @@ proc setBreakPoint(s: string, start: int) = if not hasExt(dbgTemp): add(dbgTemp, ".nim") dbgBP[x].filename = dbgTemp else: # use current filename - dbgBP[x].filename = $(||framePtr).filename + dbgBP[x].filename = $framePtr.filename # skip whitespace: while s[i] in {' ', '\t'}: inc(i) if s[i] != '\0': @@ -350,10 +346,9 @@ proc dbgStackFrame(s: string, start: int, currFrame: PExtendedFrame) = proc CommandPrompt() = # if we return from this routine, user code executes again - ThreadGlobals() var again = True - dbgFramePtr = ||framePtr # for going down and up the stack + dbgFramePtr = framePtr # for going down and up the stack dbgDown = 0 # how often we did go down while again: @@ -370,11 +365,11 @@ proc CommandPrompt() = again = false of "n", "next": dbgState = dbStepOver - dbgSkipToFrame = ||framePtr + dbgSkipToFrame = framePtr again = false of "f", "skipcurrent": dbgState = dbSkipCurrent - dbgSkipToFrame = (||framePtr).prev + dbgSkipToFrame = framePtr.prev again = false of "c", "continue": dbgState = dbBreakpoints @@ -405,7 +400,7 @@ proc CommandPrompt() = if dbgDown <= 0: debugOut("[Warning] cannot go up any further ") else: - dbgFramePtr = ||framePtr + dbgFramePtr = framePtr for j in 0 .. dbgDown-2: # BUGFIX dbgFramePtr = dbgFramePtr.prev dec(dbgDown) @@ -446,17 +441,16 @@ proc endbStep() = CommandPrompt() proc checkForBreakpoint() = - ThreadGlobals() - var i = dbgBreakpointReached((||framePtr).line) + var i = dbgBreakpointReached(framePtr.line) if i >= 0: write(stdout, "*** endb| reached ") write(stdout, dbgBP[i].name) write(stdout, " in ") - write(stdout, (||framePtr).filename) + write(stdout, framePtr.filename) write(stdout, "(") - write(stdout, (||framePtr).line) + write(stdout, framePtr.line) write(stdout, ") ") - write(stdout, (||framePtr).procname) + write(stdout, framePtr.procname) write(stdout, " ***\n") CommandPrompt() @@ -487,8 +481,7 @@ proc endb(line: int) {.compilerproc.} = # Thus, it must have as few parameters as possible to keep the # code size small! # Check if we are at an enabled breakpoint or "in the mood" - ThreadGlobals() - (||framePtr).line = line # this is done here for smaller code size! + framePtr.line = line # this is done here for smaller code size! if dbgLineHook != nil: dbgLineHook() case dbgState of dbStepInto: @@ -496,7 +489,7 @@ proc endb(line: int) {.compilerproc.} = dbgShowExecutionPoint() CommandPrompt() of dbSkipCurrent, dbStepOver: # skip current routine - if ||framePtr == dbgSkipToFrame: + if framePtr == dbgSkipToFrame: dbgShowExecutionPoint() CommandPrompt() else: # breakpoints are wanted though (I guess) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index ac4ec2f0b..6ef4ca376 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -7,16 +7,15 @@ # distribution, for details about the copyright. # -# Exception handling code. This is difficult because it has -# to work if there is no more memory (but it doesn't yet!). - -# XXX assertions are unnecessarily complex; system should use their own -# assertion mechanism instead! +# Exception handling code. Carefully coded so that tiny programs which do not +# use the heap (and nor exceptions) do not include the GC or memory allocator. var - stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>`` - ## for CGI applications - isMultiThreaded: bool # true when prog created at least 1 thread + stackTraceNewLine*: string ## undocumented feature; it is replaced by ``<br>`` + ## for CGI applications + +template stackTraceNL: expr = + (if IsNil(stackTraceNewLine): "\n" else: stackTraceNewLine) when not defined(windows) or not defined(guiapp): proc writeToStdErr(msg: CString) = write(stdout, msg) @@ -42,21 +41,6 @@ var # a global variable for the root of all try blocks currException {.rtlThreadVar.}: ref E_Base - buf {.rtlThreadVar.}: string # cannot be allocated on the stack! - assertBuf {.rtlThreadVar.}: string - # we need a different buffer for - # assert, as it raises an exception and - # exception handler needs the buffer too - gAssertionFailed {.rtlThreadVar.}: ref EAssertionFailed - -proc initGlobals() = - new(gAssertionFailed) - buf = newStringOfCap(2000) - assertBuf = newStringOfCap(2000) - -when not hasThreadSupport: - initGlobals() - proc pushFrame(s: PFrame) {.compilerRtl, inl.} = s.prev = framePtr framePtr = s @@ -83,8 +67,10 @@ proc popCurrentException {.compilerRtl, inl.} = # some platforms have native support for stack traces: const - nativeStackTraceSupported = (defined(macosx) or defined(linux)) and - not nimrodStackTrace + nativeStackTraceSupported = (defined(macosx) or defined(linux)) and + not nimrodStackTrace + hasSomeStackTrace = nimrodStackTrace or + defined(nativeStackTrace) and nativeStackTraceSupported when defined(nativeStacktrace) and nativeStackTraceSupported: type @@ -126,7 +112,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: add(s, tempDlInfo.dli_sname) else: add(s, '?') - add(s, stackTraceNewLine) + add(s, stackTraceNL) else: if dlresult != 0 and tempDlInfo.dli_sname != nil and c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32: @@ -181,24 +167,24 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = add(s, ')') for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') add(s, tempFrames[j].procname) - add(s, stackTraceNewLine) - -proc rawWriteStackTrace(s: var string) = - when nimrodStackTrace: - if framePtr == nil: - add(s, "No stack traceback available") - add(s, stackTraceNewLine) + add(s, stackTraceNL) + +when hasSomeStackTrace: + proc rawWriteStackTrace(s: var string) = + when nimrodStackTrace: + if framePtr == nil: + add(s, "No stack traceback available") + add(s, stackTraceNL) + else: + add(s, "Traceback (most recent call last)") + add(s, stackTraceNL) + auxWriteStackTrace(framePtr, s) + elif defined(nativeStackTrace) and nativeStackTraceSupported: + add(s, "Traceback from system (most recent call last)") + add(s, stackTraceNL) + auxWriteStackTraceWithBacktrace(s) else: - add(s, "Traceback (most recent call last)") - add(s, stackTraceNewLine) - auxWriteStackTrace(framePtr, s) - elif defined(nativeStackTrace) and nativeStackTraceSupported: - add(s, "Traceback from system (most recent call last)") - add(s, stackTraceNewLine) - auxWriteStackTraceWithBacktrace(s) - else: - add(s, "No stack traceback available") - add(s, stackTraceNewLine) + add(s, "No stack traceback available\n") proc quitOrDebug() {.inline.} = when not defined(endb): @@ -214,13 +200,16 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = if excHandler != nil: pushCurrentException(e) c_longjmp(excHandler.context, 1) + elif e[] is EOutOfMemory: + writeToStdErr(ename) + quitOrDebug() else: - if not isNil(buf): - setLen(buf, 0) + when hasSomeStackTrace: + var buf = newStringOfCap(2000) rawWriteStackTrace(buf) - if e.msg != nil and e.msg[0] != '\0': + if not isNil(e.msg): add(buf, "Error: unhandled exception: ") - add(buf, $e.msg) + add(buf, e.msg) else: add(buf, "Error: unhandled exception") add(buf, " [") @@ -228,7 +217,21 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = add(buf, "]\n") writeToStdErr(buf) else: - writeToStdErr(ename) + # ugly, but avoids heap allocations :-) + template xadd(buf, s, slen: expr) = + if L + slen < high(buf): + copyMem(addr(buf[L]), cstring(s), slen) + inc L, slen + template add(buf, s: expr) = + xadd(buf, s, s.len) + var buf: array [0..2000, char] + var L = 0 + add(buf, "Error: unhandled exception: ") + if not isNil(e.msg): add(buf, e.msg) + add(buf, " [") + xadd(buf, ename, c_strlen(ename)) + add(buf, "]\n") + writeToStdErr(buf) quitOrDebug() GC_enable() @@ -240,54 +243,57 @@ proc reraiseException() {.compilerRtl.} = proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} = if not cond: - #c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) - #quit(1) - GC_disable() # BUGFIX: `$` allocates a new string object! - if not isNil(assertBuf): - # BUGFIX: when debugging the GC, assertBuf may be nil - setLen(assertBuf, 0) - add(assertBuf, "[Assertion failure] file: ") - add(assertBuf, file) - add(assertBuf, " line: ") - add(assertBuf, $line) - add(assertBuf, "\n") - gAssertionFailed.msg = assertBuf - GC_enable() - if gAssertionFailed != nil: - raise gAssertionFailed - else: - c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) - quit(1) + var gAssertionFailed: ref EAssertionFailed + new(gAssertionFailed) + gAssertionFailed.msg = newStringOfCap(200) + add(gAssertionFailed.msg, "[Assertion failure] file: ") + add(gAssertionFailed.msg, file) + add(gAssertionFailed.msg, " line: ") + add(gAssertionFailed.msg, $line) + add(gAssertionFailed.msg, "\n") + raise gAssertionFailed proc WriteStackTrace() = - var s = "" - rawWriteStackTrace(s) - writeToStdErr(s) + when hasSomeStackTrace: + var s = "" + rawWriteStackTrace(s) + writeToStdErr(s) + else: + writeToStdErr("No stack traceback available\n") -var - dbgAborting: bool # whether the debugger wants to abort +when defined(endb): + var + dbgAborting: bool # whether the debugger wants to abort proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = + template processSignal(s, action: expr) = + if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") + elif s == SIGSEGV: + action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") + elif s == SIGABRT: + when defined(endb): + if dbgAborting: return # the debugger wants to abort + action("SIGABRT: Abnormal termination.\n") + elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n") + elif s == SIGILL: action("SIGILL: Illegal operation.\n") + elif s == SIGBUS: + action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") + else: action("unknown signal\n") + # print stack trace and quit - var s = sig - GC_disable() - setLen(buf, 0) - rawWriteStackTrace(buf) - - if s == SIGINT: add(buf, "SIGINT: Interrupted by Ctrl-C.\n") - elif s == SIGSEGV: - add(buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") - elif s == SIGABRT: - if dbgAborting: return # the debugger wants to abort - add(buf, "SIGABRT: Abnormal termination.\n") - elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n") - elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n") - elif s == SIGBUS: - add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") - else: add(buf, "unknown signal\n") - writeToStdErr(buf) - dbgAborting = True # play safe here... - GC_enable() + when hasSomeStackTrace: + GC_disable() + var buf = newStringOfCap(2000) + rawWriteStackTrace(buf) + processSignal(sig, buf.add) # nice hu? currying a la nimrod :-) + writeToStdErr(buf) + GC_enable() + else: + var msg: cstring + template asgn(y: expr) = msg = y + processSignal(sig, asgn) + writeToStdErr(msg) + when defined(endb): dbgAborting = True quit(1) # always quit when SIGABRT proc registerSignalHandler() = diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 619b1c9b1..29fd2eae5 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -279,7 +279,6 @@ proc initGC() = init(gch.tempStack) Init(gch.cycleRoots) Init(gch.decStack) - new(gOutOfMem) # reserve space for the EOutOfMemory exception here! proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = var d = cast[TAddress](dest) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 272a2c626..d450c520e 100755 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -52,15 +52,10 @@ const IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width IntMask = 1 shl IntShift - 1 -var - gOutOfMem: ref EOutOfMemory - -proc raiseOutOfMem() {.noreturn.} = - if gOutOfMem == nil: - echo("out of memory; cannot even throw an exception") - quit(1) - gOutOfMem.msg = "out of memory" - raise gOutOfMem +proc raiseOutOfMem() {.noinline.} = + if outOfMemHook != nil: outOfMemHook() + echo("out of memory") + quit(1) when defined(boehmgc): when defined(windows): diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index e8d351899..b0843fc11 100755 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -39,11 +39,24 @@ proc rawNewString(space: int): NimString {.compilerProc.} = (s+1) * sizeof(char))) result.space = s -proc mnewString(len: int): NimString {.exportc.} = - #c_fprintf(c_stdout, "[NEWSTRING] len: %ld\n", len) +proc mnewString(len: int): NimString {.compilerProc.} = result = rawNewString(len) result.len = len +proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = + var start = max(start, 0) + var len = min(last, s.len-1) - start + 1 + if len > 0: + result = rawNewString(len) + result.len = len + c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char)) + result.data[len] = '\0' + else: + result = mnewString(0) + +proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = + result = copyStrLast(s, start, s.len-1) + proc toNimStr(str: CString, len: int): NimString {.compilerProc.} = result = rawNewString(len) result.len = len @@ -72,20 +85,6 @@ proc hashString(s: string): int {.compilerproc.} = h = h +% h shl 15 result = h -proc copyStrLast(s: NimString, start, last: int): NimString {.exportc.} = - var start = max(start, 0) - var len = min(last, s.len-1) - start + 1 - if len > 0: - result = rawNewString(len) - result.len = len - c_memcpy(result.data, addr(s.data[start]), len * sizeof(Char)) - result.data[len] = '\0' - else: - result = mnewString(0) - -proc copyStr(s: NimString, start: int): NimString {.exportc.} = - result = copyStrLast(s, start, s.len-1) - proc addChar(s: NimString, c: char): NimString = # is compilerproc! result = s diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 7478b89d0..86a6a5691 100755 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -202,7 +202,6 @@ proc ThreadVarsAlloc(size: int): pointer = result = c_malloc(size) zeroMem(result, size) proc ThreadVarsDealloc(p: pointer) {.importc: "free", nodecl.} -proc initGlobals() type PGcThread = ptr TGcThread @@ -239,8 +238,7 @@ when not defined(useNimRtl): initStackBottom() initGC() - initGlobals() - + var heapLock: TSysLock InitSysLock(HeapLock) @@ -301,7 +299,6 @@ template ThreadProcWrapperBody(closure: expr) = setStackBottom(addr(t)) initGC() t.stackBottom = addr(t) - initGlobals() registerThread(t) try: t.fn(t.data) diff --git a/tests/accept/compile/tcodegenbug1.nim b/tests/accept/compile/tcodegenbug1.nim new file mode 100644 index 000000000..4ceaf6ebf --- /dev/null +++ b/tests/accept/compile/tcodegenbug1.nim @@ -0,0 +1,55 @@ +import os +# TODO: Rename this module to ``utils`` +type + TStatusEnum* = enum + sUnknown = -1, sBuildFailure, sBuildInProgress, sBuildSuccess, + sTestFailure, sTestInProgress, sTestSuccess, # ORDER MATTERS! + sDocGenFailure, sDocGenInProgress, sDocGenSuccess, + sCSrcGenFailure, sCSrcGenInProgress, sCSrcGenSuccess + + TStatus* = object + status*: TStatusEnum + desc*: string + hash*: string + +proc initStatus*(): TStatus = + result.status = sUnknown + result.desc = "" + result.hash = "" + +proc isInProgress*(status: TStatusEnum): bool = + return status in {sBuildInProgress, sTestInProgress, sDocGenInProgress, + sCSrcGenInProgress} + +proc `$`*(status: TStatusEnum): string = + case status + of sBuildFailure: + return "build failure" + of sBuildInProgress: + return "build in progress" + of sBuildSuccess: + return "build finished" + of sTestFailure: + return "testing failure" + of sTestInProgress: + return "testing in progress" + of sTestSuccess: + return "testing finished" + of sDocGenFailure: + return "documentation generation failed" + of sDocGenInProgress: + return "generating documentation" + of sDocGenSuccess: + return "documentation generation succeeded" + of sCSrcGenFailure: + return "csource generation failed" + of sCSrcGenInProgress: + return "csource generation in progress" + of sCSrcGenSuccess: + return "csource generation succeeded" + of sUnknown: + return "unknown" + +proc makeCommitPath*(platform, hash: string): string = + return platform / "nimrod_" & hash.copy(0, 11) # 11 Chars. + diff --git a/tests/accept/compile/tnimrodnode_for_runtime.nim b/tests/accept/compile/tnimrodnode_for_runtime.nim new file mode 100644 index 000000000..e73c8430f --- /dev/null +++ b/tests/accept/compile/tnimrodnode_for_runtime.nim @@ -0,0 +1,13 @@ +discard """ + output: "bla" + disabled: true +""" + +import macros +proc makeMacro: PNimrodNode = + result = nil + +var p = makeMacro() + +echo "bla" + diff --git a/tests/accept/run/tgenericassigntuples.nim b/tests/accept/run/tgenericassigntuples.nim new file mode 100644 index 000000000..6dd63a984 --- /dev/null +++ b/tests/accept/run/tgenericassigntuples.nim @@ -0,0 +1,16 @@ +discard """ + output: '''abc232''' +""" + +var t, s: tuple[x: string, c: int] + +proc ugh: seq[tuple[x: string, c: int]] = + result = @[("abc", 232)] + +t = ugh()[0] +s = t +s = ugh()[0] + +echo s[0], t[1] + + diff --git a/tests/accept/run/tsimplesort.nim b/tests/accept/run/tsimplesort.nim new file mode 100644 index 000000000..3ae2d06f8 --- /dev/null +++ b/tests/accept/run/tsimplesort.nim @@ -0,0 +1,346 @@ +discard """ + output: '''true''' +""" + +import hashes, math + + +when defined(shallowADT): + {.pragma: myShallow, shallow.} +else: + {.pragma: myShallow.} + +type + TSlotEnum = enum seEmpty, seFilled, seDeleted + TKeyValuePair[A, B] = tuple[slot: TSlotEnum, key: A, val: B] + TKeyValuePairSeq[A, B] = seq[TKeyValuePair[A, B]] + TTable* {.final, myShallow.}[A, B] = object + data: TKeyValuePairSeq[A, B] + counter: int + +proc len*[A, B](t: TTable[A, B]): int = + ## returns the number of keys in `t`. + result = t.counter + +iterator pairs*[A, B](t: TTable[A, B]): tuple[key: A, val: B] = + ## iterates over any (key, value) pair in the table `t`. + for h in 0..high(t.data): + if t.data[h].slot == seFilled: yield (t.data[h].key, t.data[h].val) + +iterator keys*[A, B](t: TTable[A, B]): A = + ## iterates over any key in the table `t`. + for h in 0..high(t.data): + if t.data[h].slot == seFilled: yield t.data[h].key + +iterator values*[A, B](t: TTable[A, B]): B = + ## iterates over any value in the table `t`. + for h in 0..high(t.data): + if t.data[h].slot == seFilled: yield t.data[h].val + +const + growthFactor = 2 + +proc mustRehash(length, counter: int): bool {.inline.} = + assert(length > counter) + result = (length * 2 < counter * 3) or (length - counter < 4) + +proc nextTry(h, maxHash: THash): THash {.inline.} = + result = ((5 * h) + 1) and maxHash + +template rawGetImpl() = + var h: THash = hash(key) and high(t.data) # start with real hash value + while t.data[h].slot != seEmpty: + if t.data[h].key == key and t.data[h].slot == seFilled: + return h + h = nextTry(h, high(t.data)) + result = -1 + +template rawInsertImpl() = + var h: THash = hash(key) and high(data) + while data[h].slot == seFilled: + h = nextTry(h, high(data)) + data[h].key = key + data[h].val = val + data[h].slot = seFilled + +proc RawGet[A, B](t: TTable[A, B], key: A): int = + rawGetImpl() + +proc `[]`*[A, B](t: TTable[A, B], key: A): B = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, + ## default empty value for the type `B` is returned + ## and no exception is raised. One can check with ``hasKey`` whether the key + ## exists. + var index = RawGet(t, key) + if index >= 0: result = t.data[index].val + +proc hasKey*[A, B](t: TTable[A, B], key: A): bool = + ## returns true iff `key` is in the table `t`. + result = rawGet(t, key) >= 0 + +proc RawInsert[A, B](t: var TTable[A, B], data: var TKeyValuePairSeq[A, B], + key: A, val: B) = + rawInsertImpl() + +proc Enlarge[A, B](t: var TTable[A, B]) = + var n: TKeyValuePairSeq[A, B] + newSeq(n, len(t.data) * growthFactor) + for i in countup(0, high(t.data)): + if t.data[i].slot == seFilled: RawInsert(t, n, t.data[i].key, t.data[i].val) + swap(t.data, n) + +template PutImpl() = + var index = RawGet(t, key) + if index >= 0: + t.data[index].val = val + else: + if mustRehash(len(t.data), t.counter): Enlarge(t) + RawInsert(t, t.data, key, val) + inc(t.counter) + +proc `[]=`*[A, B](t: var TTable[A, B], key: A, val: B) = + ## puts a (key, value)-pair into `t`. + putImpl() + +proc del*[A, B](t: var TTable[A, B], key: A) = + ## deletes `key` from hash table `t`. + var index = RawGet(t, key) + if index >= 0: + t.data[index].slot = seDeleted + dec(t.counter) + +proc initTable*[A, B](initialSize=64): TTable[A, B] = + ## creates a new hash table that is empty. `initialSize` needs to be + ## a power of two. + assert isPowerOfTwo(initialSize) + result.counter = 0 + newSeq(result.data, initialSize) + +proc toTable*[A, B](pairs: openarray[tuple[key: A, + val: B]]): TTable[A, B] = + ## creates a new hash table that contains the given `pairs`. + result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) + for key, val in items(pairs): result[key] = val + +template dollarImpl(): stmt = + if t.len == 0: + result = "{:}" + else: + result = "{" + for key, val in pairs(t): + if result.len > 1: result.add(", ") + result.add($key) + result.add(": ") + result.add($val) + result.add("}") + +proc `$`*[A, B](t: TTable[A, B]): string = + ## The `$` operator for hash tables. + dollarImpl() + +# ------------------------------ count tables ------------------------------- + +type + TCountTable* {.final, myShallow.}[ + A] = object ## table that counts the number of each key + data: seq[tuple[key: A, val: int]] + counter: int + +proc len*[A](t: TCountTable[A]): int = + ## returns the number of keys in `t`. + result = t.counter + +iterator pairs*[A](t: TCountTable[A]): tuple[key: A, val: int] = + ## iterates over any (key, value) pair in the table `t`. + for h in 0..high(t.data): + if t.data[h].val != 0: yield (t.data[h].key, t.data[h].val) + +iterator keys*[A](t: TCountTable[A]): A = + ## iterates over any key in the table `t`. + for h in 0..high(t.data): + if t.data[h].val != 0: yield t.data[h].key + +iterator values*[A](t: TCountTable[A]): int = + ## iterates over any value in the table `t`. + for h in 0..high(t.data): + if t.data[h].val != 0: yield t.data[h].val + +proc RawGet[A](t: TCountTable[A], key: A): int = + var h: THash = hash(key) and high(t.data) # start with real hash value + while t.data[h].val != 0: + if t.data[h].key == key: return h + h = nextTry(h, high(t.data)) + result = -1 + +proc `[]`*[A](t: TCountTable[A], key: A): int = + ## retrieves the value at ``t[key]``. If `key` is not in `t`, + ## 0 is returned. One can check with ``hasKey`` whether the key + ## exists. + var index = RawGet(t, key) + if index >= 0: result = t.data[index].val + +proc hasKey*[A](t: TCountTable[A], key: A): bool = + ## returns true iff `key` is in the table `t`. + result = rawGet(t, key) >= 0 + +proc RawInsert[A](t: TCountTable[A], data: var seq[tuple[key: A, val: int]], + key: A, val: int) = + var h: THash = hash(key) and high(data) + while data[h].val != 0: h = nextTry(h, high(data)) + data[h].key = key + data[h].val = val + +proc Enlarge[A](t: var TCountTable[A]) = + var n: seq[tuple[key: A, val: int]] + newSeq(n, len(t.data) * growthFactor) + for i in countup(0, high(t.data)): + if t.data[i].val != 0: RawInsert(t, n, t.data[i].key, t.data[i].val) + swap(t.data, n) + +proc `[]=`*[A](t: var TCountTable[A], key: A, val: int) = + ## puts a (key, value)-pair into `t`. `val` has to be positive. + assert val > 0 + PutImpl() + +proc initCountTable*[A](initialSize=64): TCountTable[A] = + ## creates a new count table that is empty. `initialSize` needs to be + ## a power of two. + assert isPowerOfTwo(initialSize) + result.counter = 0 + newSeq(result.data, initialSize) + +proc toCountTable*[A](keys: openArray[A]): TCountTable[A] = + ## creates a new count table with every key in `keys` having a count of 1. + result = initCountTable[A](nextPowerOfTwo(keys.len+10)) + for key in items(keys): result[key] = 1 + +proc `$`*[A](t: TCountTable[A]): string = + ## The `$` operator for count tables. + dollarImpl() + +proc inc*[A](t: var TCountTable[A], key: A, val = 1) = + ## increments `t[key]` by `val`. + var index = RawGet(t, key) + if index >= 0: + inc(t.data[index].val, val) + else: + if mustRehash(len(t.data), t.counter): Enlarge(t) + RawInsert(t, t.data, key, val) + inc(t.counter) + +proc Smallest*[A](t: TCountTable[A]): tuple[key: A, val: int] = + ## returns the largest (key,val)-pair. Efficiency: O(n) + assert t.len > 0 + var minIdx = 0 + for h in 1..high(t.data): + if t.data[h].val > 0 and t.data[minIdx].val > t.data[h].val: minIdx = h + result.key = t.data[minIdx].key + result.val = t.data[minIdx].val + +proc Largest*[A](t: TCountTable[A]): tuple[key: A, val: int] = + ## returns the (key,val)-pair with the largest `val`. Efficiency: O(n) + assert t.len > 0 + var maxIdx = 0 + for h in 1..high(t.data): + if t.data[maxIdx].val < t.data[h].val: maxIdx = h + result.key = t.data[maxIdx].key + result.val = t.data[maxIdx].val + +proc sort*[A](t: var TCountTable[A]) = + ## sorts the count table so that the entry with the highest counter comes + ## first. This is destructive! You must not modify `t` afterwards! + ## You can use the iterators `pairs`, `keys`, and `values` to iterate over + ## `t` in the sorted order. + var w: type(t.data[0]) + checkTypeInfo(addr(w), 10, GetTypeInfo(w)) + + # we use shellsort here; fast enough and simple + var h = 1 + while true: + h = 3 * h + 1 + if h >= high(t.data): break + while true: + h = h div 3 + for i in countup(h, high(t.data)): + var j = i + checkTypeInfo(addr(w), 11, GetTypeInfo(w)) + while t.data[j-h].val <= t.data[j].val: + checkTypeInfo(addr(w), 12, GetTypeInfo(w)) + var xyz = t.data[j] + checkTypeInfo(addr(w), 13, GetTypeInfo(w)) + t.data[j] = xyz + checkTypeInfo(addr(w), 14, GetTypeInfo(w)) + t.data[j-h] = t.data[j] + #swap(t.data[j], t.data[j-h]) + checkTypeInfo(addr(w), 15, GetTypeInfo(w)) + j = j-h + checkTypeInfo(addr(w), 16, GetTypeInfo(w)) + if j < h: break + if h == 1: break + + +const + data = { + "34": 123456, "12": 789, + "90": 343, "0": 34404, + "1": 344004, "2": 344774, + "3": 342244, "4": 3412344, + "5": 341232144, "6": 34214544, + "7": 3434544, "8": 344544, + "9": 34435644, "---00": 346677844, + "10": 34484, "11": 34474, "19": 34464, + "20": 34454, "30": 34141244, "40": 344114, + "50": 344490, "60": 344491, "70": 344492, + "80": 344497} + +proc fake() = + #var s: TTable[string, int] + #var otherCountTable: TCountTable[string] + #var w: type(otherCountTable.data[0]) + #checkTypeInfo(addr(w), nil, GetTypeInfo(w)) + +proc countTableTest1 = + #fake() + var s = initTable[string, int](64) + for key, val in items(data): s[key] = val + var w: tuple[key: string, val: int] #type(otherCountTable.data[0]) + checkTypeInfo(addr(w), 0, GetTypeInfo(w)) + + #var s = data.toTable + #var otherCountTable: TCountTable[string] + #var w: tuple[key: string, val: int] #type(otherCountTable.data[0]) + checkTypeInfo(addr(w), 1, GetTypeInfo(w)) + + var t = initCountTable[string]() + checkTypeInfo(addr(w), 2, GetTypeInfo(w)) + for k, v in items(data): t.inc(k) + checkTypeInfo(addr(w), 3, GetTypeInfo(w)) + for k in t.keys: assert t[k] == 1 + checkTypeInfo(addr(w), 4, GetTypeInfo(w)) + t.inc("90", 3) + checkTypeInfo(addr(w), 5, GetTypeInfo(w)) + t.inc("12", 2) + checkTypeInfo(addr(w), 6, GetTypeInfo(w)) + t.inc("34", 1) + checkTypeInfo(addr(w), 7, GetTypeInfo(w)) + assert t.largest()[0] == "90" + checkTypeInfo(addr(w), 8, GetTypeInfo(w)) + t.sort() + checkTypeInfo(addr(w), 9, GetTypeInfo(w)) + when false: + for k, v in t.pairs: + echo k, ": ", v + + var i = 0 + for k, v in t.pairs: + case i + of 0: assert k == "90" and v == 4 + of 1: assert k == "12" and v == 3 + of 2: assert k == "34" and v == 2 + else: break + inc i + +countTableTest1() +echo true + + diff --git a/tests/reject/temptycaseobj.nim b/tests/reject/temptycaseobj.nim new file mode 100644 index 000000000..5977cb92b --- /dev/null +++ b/tests/reject/temptycaseobj.nim @@ -0,0 +1,14 @@ +discard """ + line: 11 + errormsg: "identifier expected, but found '[same indentation]'" +""" + +type + TMyEnum = enum enA, enU, enO + TMyCase = object + case e: TMyEnum + of enA: + of enU: x, y: int + of enO: a, b: string + + diff --git a/tests/reject/tprocvar.nim b/tests/reject/tprocvar.nim new file mode 100644 index 000000000..56f76c613 --- /dev/null +++ b/tests/reject/tprocvar.nim @@ -0,0 +1,18 @@ +discard """ + errormsg: "type mismatch" + line: 17 + file: "tprocvar.nim" +""" + +type + TCallback = proc (a, b: int) + +proc huh(x, y: var int) = + x = 0 + y = x+1 + +proc so(c: TCallback) = + c(2, 4) + +so(huh) + diff --git a/tests/stckovfl.nim b/tests/stckovfl.nim index 799fe0001..205d73edf 100755 --- a/tests/stckovfl.nim +++ b/tests/stckovfl.nim @@ -1,7 +1,9 @@ # To test stack overflow message proc over(a: int): int = - if a == high(int): return + if a >= 400: + assert false + return result = over(a+1)+5 Echo($over(0)) diff --git a/todo.txt b/todo.txt index 5e1788aac..3308309c7 100755 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,6 @@ High priority (version 0.8.12) ============================== * test threads on windows; thread analysis needs to be even more restrictive! * implement message passing built-ins: channels/queues -* outOfMem hook; 0 over-head for hello world program :-) * bug: {:}.toTable[int, string]() @@ -20,18 +19,6 @@ version 0.9.0 - fix overloading resolution - make ^ available as operator - implement closures; implement proper coroutines -- rethink comment syntax; people want:: - - type - TConfig* = object - limit*: int - outFile*: string - # Regex patterns to ignore. TLogEntry field -> regex pattern - # If this matches, the request will NOT be shown. - ignoreRe*: TTable[string, string] - - I think a good compromise is to define positions for the docgen in the - grammar and ignore other comments everywhere. Bugs ---- @@ -45,8 +32,7 @@ version 0.9.XX ============== - distinct types for array/seq indexes -- GC: marker procs for native Nimrod GC and Boehm GC -- code concerning 'assert' is wasteful and unnecessarily complex +- GC: marker procs for native Nimrod GC and Boehm GC; precise stack marking - implicit ref/ptr->var conversion; the compiler may store an object implicitly on the heap for write barrier efficiency - resizing of strings/sequences could take into account the memory that diff --git a/web/news.txt b/web/news.txt index a7f6a0b78..cb41dba7c 100755 --- a/web/news.txt +++ b/web/news.txt @@ -95,7 +95,7 @@ Additions - Added a wrapper for ``sphinx``. - The compiler now supports array, sequence and string slicing. - Added ``system.newStringOfCap``. -- Added ``system.raiseHook`` and ``system.outOfMemoryHook``. +- Added ``system.raiseHook`` and ``system.outOfMemHook``. - Added ``system.writeFile``. - Added ``system.shallowCopy``. - ``system.echo`` is guaranteed to be thread-safe. |