summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2011-01-29 01:47:58 +0100
committerAraq <rumpf_a@web.de>2011-01-29 01:47:58 +0100
commitf46870fe1ce3a28ab44417effd1c684522568a8d (patch)
tree446e3b8cb383cc4cabb1b508a3e5d8a9ed443e67
parent557adbcaac45d7b9c92904349c6cc3a7a8282ed7 (diff)
downloadNim-f46870fe1ce3a28ab44417effd1c684522568a8d.tar.gz
changes to threads; --recursivePath support
-rwxr-xr-xconfig/nimrod.cfg1
-rwxr-xr-xdoc/advopt.txt1
-rwxr-xr-xdoc/basicopt.txt2
-rw-r--r--lib/core/threads.nim255
-rwxr-xr-xlib/system/cgprocs.nim26
-rwxr-xr-xrod/cgen.nim14
-rwxr-xr-xrod/commands.nim29
-rwxr-xr-xrod/lookups.nim89
-rwxr-xr-xrod/options.nim8
-rwxr-xr-xrod/semexprs.nim10
-rwxr-xr-xrod/semgnrc.nim36
-rwxr-xr-xrod/seminst.nim8
-rwxr-xr-xrod/semstmts.nim16
-rwxr-xr-xrod/semtypes.nim2
-rwxr-xr-xrod/sigmatch.nim32
-rwxr-xr-xrod/types.nim4
-rwxr-xr-xrod/wordrecg.nim6
-rwxr-xr-xtodo.txt5
-rwxr-xr-xtools/install.tmpl22
-rwxr-xr-xweb/news.txt4
20 files changed, 393 insertions, 177 deletions
diff --git a/config/nimrod.cfg b/config/nimrod.cfg
index 4914d0993..96504b483 100755
--- a/config/nimrod.cfg
+++ b/config/nimrod.cfg
@@ -30,6 +30,7 @@ path="$lib/windows"
 path="$lib/posix"
 path="$lib/ecmas"
 path="$lib/pure/unidecode"
+#recursivePath:"$user/.babel/lib" 
 
 @if release or quick:
   obj_checks:off
diff --git a/doc/advopt.txt b/doc/advopt.txt
index cf0839386..fd624d93e 100755
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -22,6 +22,7 @@ Advanced options:
   --os:SYMBOL               set the target operating system (cross-compilation)
   --cpu:SYMBOL              set the target processor (cross-compilation)
   --debuginfo               enables debug information
+  --debugger:on|off         turn Embedded Nimrod Debugger on|off
   -t, --passc:OPTION        pass an option to the C compiler
   -l, --passl:OPTION        pass an option to the linker
   --genMapping              generate a mapping file containing
diff --git a/doc/basicopt.txt b/doc/basicopt.txt
index 7d2a9b26d..45efff4d0 100755
--- a/doc/basicopt.txt
+++ b/doc/basicopt.txt
@@ -18,7 +18,7 @@ Options:
   -f, --forceBuild          force rebuilding of all modules
   --stackTrace:on|off       turn stack tracing on|off
   --lineTrace:on|off        turn line tracing on|off
-  --debugger:on|off         turn Embedded Nimrod Debugger on|off
+  --threads:on|off          turn support for multi-threading on|off
   -x, --checks:on|off       turn all runtime checks on|off
   --objChecks:on|off        turn obj conversion checks on|off
   --fieldChecks:on|off      turn case variant field checks on|off
diff --git a/lib/core/threads.nim b/lib/core/threads.nim
index feb026547..fc120f1a3 100644
--- a/lib/core/threads.nim
+++ b/lib/core/threads.nim
@@ -16,11 +16,11 @@
 ## .. code-block:: nimrod
 ##
 ##  var
-##    thr: array [0..4, TThread]
+##    thr: array [0..4, TThread[tuple[a,b: int]]]
 ##    L: TLock
 ##  
-##  proc threadFunc(c: pointer) {.procvar.} = 
-##    for i in 0..9: 
+##  proc threadFunc(interval: tuple[a,b: int]) {.procvar.} = 
+##    for i in interval.a..interval.b: 
 ##      Aquire(L) # lock stdout
 ##      echo i
 ##      Release(L)
@@ -28,10 +28,12 @@
 ##  InitLock(L)
 ##
 ##  for i in 0..high(thr):
-##    createThread(thr[i], threadFunc)
+##    createThread(thr[i], threadFunc, (i*10, i*10+5))
 ##  for i in 0..high(thr):
 ##    joinThread(thr[i])
 
+when not defined(boehmgc) and not defined(nogc):
+  {.error: "Thread support requires --gc:boehm or --gc:none".}
 
 # We jump through some hops here to ensure that Nimrod thread procs can have
 # the Nimrod calling convention. This is needed because thread procs are 
@@ -41,10 +43,9 @@
 # GC'ed closures in Nimrod.
 
 type
-  TThreadProc* = proc (closure: pointer) ## Standard Nimrod thread proc.
-  TThreadProcClosure {.pure, final.} = object
-    fn: TThreadProc
-    data: pointer
+  TThreadProcClosure {.pure, final.}[TParam] = object
+    fn: proc (p: TParam)
+    data: TParam
   
 when defined(Windows):
   type 
@@ -60,17 +61,22 @@ when defined(Windows):
       
     TWinThreadProc = proc (x: pointer): int32 {.stdcall.}
     
-    TLock* = TSysLock ## Standard Nimrod Lock type.
-  
-  proc InitLock*(L: var TLock) {.stdcall,
+  proc InitSysLock(L: var TSysLock) {.stdcall,
     dynlib: "kernel32", importc: "InitializeCriticalSection".}
     ## Initializes the lock `L`.
 
-  proc Aquire*(L: var TLock) {.stdcall,
+  proc TryAquireSysAux(L: var TSysLock): int32 {.stdcall,
+    dynlib: "kernel32", importc: "TryEnterCriticalSection".}
+    ## Tries to aquire the lock `L`.
+    
+  proc TryAquireSys(L: var TSysLock): bool {.inline.} = 
+    result = TryAquireSysAux(L) != 0'i32
+
+  proc AquireSys(L: var TSysLock) {.stdcall,
     dynlib: "kernel32", importc: "EnterCriticalSection".}
     ## Aquires the lock `L`.
     
-  proc Release*(L: var TLock) {.stdcall,
+  proc ReleaseSys(L: var TSysLock) {.stdcall,
     dynlib: "kernel32", importc: "LeaveCriticalSection".}
     ## Releases the lock `L`.
 
@@ -99,8 +105,8 @@ when defined(Windows):
   proc TerminateThread(hThread: THandle, dwExitCode: int32): int32 {.
     stdcall, dynlib: "kernel32", importc: "TerminateThread".}
 
-  proc threadProcWrapper(closure: pointer): int32 {.stdcall.} = 
-    var c = cast[ptr TThreadProcClosure](closure)
+  proc threadProcWrapper[TParam](closure: pointer): int32 {.stdcall.} = 
+    var c = cast[ptr TThreadProcClosure[TParam]](closure)
     c.fn(c.data)
     # implicitely return 0
 
@@ -109,16 +115,18 @@ else:
     TSysLock {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = int
     TSysThread {.importc: "pthread_t", header: "<sys/types.h>".} = int
     
-    TLock* = TSysLock
-
-  proc InitLockAux(L: var TSysLock, attr: pointer = nil) {.
+  proc InitSysLock(L: var TSysLock, attr: pointer = nil) {.
     importc: "pthread_mutex_init", header: "<pthread.h>".}
 
-  proc InitLock*(L: var TLock) {.inline.} = 
-    InitLockAux(L)
-  proc Aquire*(L: var TLock) {.
+  proc AquireSys(L: var TSysLock) {.
     importc: "pthread_mutex_lock", header: "<pthread.h>".}
-  proc Release*(L: var TLock) {.
+  proc TryAquireSysAux(L: var TSysLock): cint {.
+    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
+
+  proc TryAquireSys(L: var TSysLock): bool {.inline.} = 
+    result = TryAquireSysAux(L) == 0'i32
+
+  proc ReleaseSys(L: var TSysLock) {.
     importc: "pthread_mutex_unlock", header: "<pthread.h>".}
 
   proc pthread_create(a1: var TSysThread, a2: ptr int,
@@ -131,44 +139,205 @@ else:
   proc pthread_cancel(a1: TSysThread): cint {.
     importc: "pthread_cancel", header: "<pthread.h>".}
 
-  proc threadProcWrapper(closure: pointer) {.noconv.} = 
-    var c = cast[ptr TThreadProcClosure](closure)
+  proc threadProcWrapper[TParam](closure: pointer) {.noconv.} = 
+    var c = cast[ptr TThreadProcClosure[TParam]](closure)
     c.fn(c.data)
 
   {.passL: "-pthread".}
   {.passC: "-pthread".}
 
+const
+  noDeadlocks = true # compileOption("deadlockPrevention")
+  
+when noDeadLocks:
+  type
+    TLock* {.pure, final.} = object ## Standard Nimrod Lock type.
+      key: int       # used for identity and global order!
+      sys: TSysLock
+      next: ptr TLock
+else:
+  type 
+    TLock* = TSysLock    
+    
 type
-  TThread* = object of TObject ## Nimrod thread.
+  TThread* {.pure, final.}[TParam] = object ## Nimrod thread.
     sys: TSysThread
-    c: TThreadProcClosure
+    c: TThreadProcClosure[TParam]
+
+
+when nodeadlocks:
+  var 
+    lockList {.threadvar.}: ptr TLock
+    deadlocksPrevented* = 0  ## counts the number of times a 
+                             ## deadlock has been prevented
+
+proc InitLock*(L: var TLock) {.inline.} =
+  ## Initializes the lock `L`.
+  when noDeadlocks:
+    InitSysLock(L.sys)
+    L.key = cast[int](addr(L))
+  else:
+    InitSysLock(L)
+
+proc TryAquire*(L: var TLock): bool {.inline.} = 
+  ## Try to aquires the lock `L`. Returns `true` on success.
+  when noDeadlocks:
+    result = TryAquireSys(L.sys)
+  else:
+    result = TryAquireSys(L)
 
+proc Aquire*(L: var TLock) =
+  ## Aquires the lock `L`.
+  when nodeadlocks:
+    # Note: we MUST NOT change the linked list of locks before we have aquired
+    # the proper locks! This is because the pointer to the next lock is part
+    # of the lock itself!
+    assert L.key != 0
+    var p = lockList
+    if p == nil:
+      # simple case: no lock aquired yet:
+      AquireSys(L.sys)
+      locklist = addr(L)
+      L.next = nil
+    else:
+      # check where to put L into the list:
+      var r = p
+      var last: ptr TLock = nil
+      while L.key < r.key: 
+        if r.next == nil: 
+          # best case: L needs to be aquired as last lock, so we can 
+          # skip a good amount of work: 
+          AquireSys(L.sys)
+          r.next = addr(L)
+          L.next = nil
+          return
+        last = r
+        r = r.next
+      # special case: thread already holds L!
+      if L.key == r.key: return
+      
+      # bad case: L needs to be somewhere in between
+      # release all locks after L: 
+      var rollback = r
+      while r != nil:
+        ReleaseSys(r.sys)
+        r = r.next
+      # and aquire them in the correct order again:
+      AquireSys(L.sys)
+      r = rollback
+      while r != nil:
+        assert r.key < L.key
+        AquireSys(r.sys)
+        r = r.next
+      # now that we have all the locks we need, we can insert L 
+      # into our list:
+      if last != nil:
+        L.next = last.next
+        last.next = addr(L)
+      else:
+        L.next = lockList
+        lockList = addr(L)
+      inc(deadlocksPrevented)
+  else:
+    AquireSys(L)
   
-proc createThread*(t: var TThread, tp: TThreadProc, 
-                   closure: pointer = nil) = 
-  ## creates a new thread `t` and starts its execution. Entry point is the
-  ## proc `tp`. `closure` is passed to `tp`.
-  t.c.data = closure
-  t.c.fn = tp
-  when defined(windows):
-    var dummyThreadId: int32
-    t.sys = CreateThread(nil, 0'i32, threadProcWrapper, addr(t.c), 0'i32, 
-                         dummyThreadId)
-  else: 
-    discard pthread_create(t.sys, nil, threadProcWrapper, addr(t.c))
+proc Release*(L: var TLock) =
+  ## Releases the lock `L`.
+  when nodeadlocks:
+    assert L.key != 0
+    var p = lockList
+    var last: ptr TLock = nil
+    while true:
+      # if we don't find the lock, die by reading from nil!
+      if p.key == L.key: 
+        if last != nil:
+          last.next = p.next
+        else:
+          assert p == lockList
+          lockList = locklist.next
+        L.next = nil
+        break
+      last = p
+      p = p.next
+    ReleaseSys(L.sys)
+  else:
+    ReleaseSys(L)
 
-proc joinThread*(t: TThread) = 
+proc joinThread*[TParam](t: TThread[TParam]) {.inline.} = 
   ## waits for the thread `t` until it has terminated.
-  when defined(windows):
+  when hostOS == "windows":
     discard WaitForSingleObject(t.sys, -1'i32)
   else:
     discard pthread_join(t.sys, nil)
 
-proc destroyThread*(t: var TThread) =
+proc destroyThread*[TParam](t: var TThread[TParam]) {.inline.} =
   ## forces the thread `t` to terminate. This is potentially dangerous if
-  ## you don't have full control over `t` and its aquired ressources.
-  when defined(windows):
+  ## you don't have full control over `t` and its aquired resources.
+  when hostOS == "windows":
     discard TerminateThread(t.sys, 1'i32)
   else:
     discard pthread_cancel(t.sys)
 
+proc createThread*[TParam](t: var TThread[TParam], 
+                           tp: proc (param: TParam), 
+                           param: TParam) = 
+  ## creates a new thread `t` and starts its execution. Entry point is the
+  ## proc `tp`. `param` is passed to `tp`.
+  t.c.data = param
+  t.c.fn = tp
+  when hostOS == "windows":
+    var dummyThreadId: int32
+    t.sys = CreateThread(nil, 0'i32, threadProcWrapper[TParam], 
+                         addr(t.c), 0'i32, dummyThreadId)
+  else: 
+    discard pthread_create(t.sys, nil, threadProcWrapper[TParam], addr(t.c))
+
+when isMainModule:
+  var
+    thr: array [0..4, TThread[tuple[a,b: int]]]
+    L, M, N: TLock
+  
+  proc threadFunc(interval: tuple[a,b: int]) {.procvar.} = 
+    for i in interval.a..interval.b: 
+      case i mod 6
+      of 0:
+        Aquire(L) # lock stdout
+        Aquire(M)
+        Aquire(N)
+      of 1:
+        Aquire(L)
+        Aquire(N) # lock stdout
+        Aquire(M)
+      of 2:
+        Aquire(M)
+        Aquire(L)
+        Aquire(N)
+      of 3:
+        Aquire(M)
+        Aquire(N)
+        Aquire(L)
+      of 4:
+        Aquire(N)
+        Aquire(M)
+        Aquire(L)
+      of 5:
+        Aquire(N)
+        Aquire(L)
+        Aquire(M)
+      else: assert false
+      echo i
+      echo "deadlocks prevented: ", deadlocksPrevented
+      Release(L)
+      Release(M)
+      Release(N)
+
+  InitLock(L)
+  InitLock(M)
+  InitLock(N)
+
+  for i in 0..high(thr):
+    createThread(thr[i], threadFunc, (i*100, i*100+50))
+  for i in 0..high(thr):
+    joinThread(thr[i])
+
+
diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim
index cabdcafc4..945ce4692 100755
--- a/lib/system/cgprocs.nim
+++ b/lib/system/cgprocs.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nimrod's Runtime Library
-#        (c) Copyright 2010 Andreas Rumpf
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -24,3 +24,27 @@ proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
 proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline.}
 
 
+# Support for thread local storage:
+when false:
+  when defined(windows):
+    proc TlsAlloc(): int32 {.importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
+    proc TlsSetValue(dwTlsIndex: int32, lpTlsValue: pointer) {.
+      importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
+    proc TlsGetValue(dwTlsIndex: int32): pointer {.
+      importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
+    
+  else:
+    type
+      Tpthread_key {.importc: "pthread_key_t", header: "<sys/types.h>".} = int
+
+    proc pthread_getspecific(a1: Tpthread_key): pointer {.
+      importc: "pthread_getspecific", header: "<pthread.h>".}
+    proc pthread_key_create(a1: ptr Tpthread_key, 
+                            a2: proc (x: pointer) {.noconv.}): int32 {.
+      importc: "pthread_key_create", header: "<pthread.h>".}
+    proc pthread_key_delete(a1: Tpthread_key): int32 {.
+      importc: "pthread_key_delete", header: "<pthread.h>".}
+
+    proc pthread_setspecific(a1: Tpthread_key, a2: pointer): int32 {.
+      importc: "pthread_setspecific", header: "<pthread.h>".}
+
diff --git a/rod/cgen.nim b/rod/cgen.nim
index f99a06a97..685d912c4 100755
--- a/rod/cgen.nim
+++ b/rod/cgen.nim
@@ -380,14 +380,20 @@ proc assignLocalVar(p: BProc, s: PSym) =
     appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
   localDebugInfo(p, s)
 
+proc declareThreadVar(m: BModule, s: PSym) =
+  if optThreads in gGlobalOptions:
+    app(m.s[cfsVars], "NIM_THREADVAR ")
+  app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
+  
+
 proc assignGlobalVar(p: BProc, s: PSym) = 
   if s.loc.k == locNone: 
     fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
   useHeader(p.module, s)
   if lfNoDecl in s.loc.flags: return 
   if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
-  if sfThreadVar in s.flags: app(p.module.s[cfsVars], "NIM_THREADVAR ")
-  app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
+  if sfThreadVar in s.flags: declareThreadVar(p.module, s)
+  else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
   if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
   if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
   appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
@@ -686,8 +692,8 @@ proc genVarPrototype(m: BModule, sym: PSym) =
            [sym.loc.r, getTypeDesc(m, sym.loc.t)])
     else: 
       app(m.s[cfsVars], "extern ")
-      if sfThreadVar in sym.flags: app(m.s[cfsVars], "NIM_THREADVAR ")
-      app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
+      if sfThreadVar in sym.flags: declareThreadVar(m, sym)
+      else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
       if sfRegister in sym.flags: app(m.s[cfsVars], " register")
       if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
       appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
diff --git a/rod/commands.nim b/rod/commands.nim
index ffd117cd8..7a74cac7f 100755
--- a/rod/commands.nim
+++ b/rod/commands.nim
@@ -25,7 +25,7 @@ proc ProcessCommand*(switch: string, pass: TCmdLinePass)
 proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo)
 # implementation
 
-const 
+const
   HelpMessage = "Nimrod Compiler Version $1 (" & compileDate & ") [$2: $3]\n" &
       "Copyright (c) 2004-2011 by Andreas Rumpf\n"
 
@@ -49,7 +49,7 @@ Options:
   -f, --forceBuild          force rebuilding of all modules
   --stackTrace:on|off       turn stack tracing on|off
   --lineTrace:on|off        turn line tracing on|off
-  --debugger:on|off         turn Embedded Nimrod Debugger on|off
+  --threads:on|off          turn support for multi-threading on|off
   -x, --checks:on|off       turn all runtime checks on|off
   --objChecks:on|off        turn obj conversion checks on|off
   --fieldChecks:on|off      turn case variant field checks on|off
@@ -92,6 +92,7 @@ Advanced options:
   --os:SYMBOL               set the target operating system (cross-compilation)
   --cpu:SYMBOL              set the target processor (cross-compilation)
   --debuginfo               enables debug information
+  --debugger:on|off         turn Embedded Nimrod Debugger on|off
   -t, --passc:OPTION        pass an option to the C compiler
   -l, --passl:OPTION        pass an option to the linker
   --genMapping              generate a mapping file containing
@@ -212,9 +213,6 @@ proc ProcessSpecificNote(arg: string, state: TSpecialWord, pass: TCmdlinePass,
   of wOn: incl(gNotes, n)
   of wOff: excl(gNotes, n)
   else: liMessage(info, errOnOrOffExpectedButXFound, arg)
-  
-proc processPath(path: string): string = 
-  result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath])
 
 proc processCompile(filename: string) = 
   var found = findFile(filename)
@@ -269,20 +267,32 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
   of wRun, wR: result = contains(gGlobalOptions, optRun)
   of wSymbolFiles: result = contains(gGlobalOptions, optSymbolFiles)
   of wGenScript: result = contains(gGlobalOptions, optGenScript)
+  of wThreads: result = contains(gGlobalOptions, optThreads)
   else: InvalidCmdLineOption(passCmd1, switch, info)
+  
+proc processPath(path: string): string = 
+  result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath,
+    "home", removeTrailingDirSep(os.getHomeDir())])
+
+proc addPath(path: string) = 
+  if not contains(options.searchPaths, path): 
+    lists.PrependStr(options.searchPaths, path)
 
 proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = 
   var 
     theOS: TSystemOS
     cpu: TSystemCPU
-    key, val, path: string
+    key, val: string
   case whichKeyword(switch)
   of wPath, wP: 
     expectArg(switch, arg, pass, info)
-    path = processPath(arg)
-    if not contains(options.searchPaths, path): 
-      lists.PrependStr(options.searchPaths, path)
+    addPath(processPath(arg))
     #discard lists.IncludeStr(options.searchPaths, path)
+  of wRecursivePath:
+    expectArg(switch, arg, pass, info)
+    var path = processPath(arg)
+    for p in os.walkDirRec(path, {pcDir}): addPath(p)
+    addPath(path)
   of wOut, wO: 
     expectArg(switch, arg, pass, info)
     options.outFile = arg
@@ -356,6 +366,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
   of wLineDir: ProcessOnOffSwitch({optLineDir}, arg, pass, info)
   of wAssertions, wA: ProcessOnOffSwitch({optAssert}, arg, pass, info)
   of wDeadCodeElim: ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
+  of wThreads: ProcessOnOffSwitchG({optThreads}, arg, pass, info)
   of wOpt: 
     expectArg(switch, arg, pass, info)
     case whichKeyword(arg)
diff --git a/rod/lookups.nim b/rod/lookups.nim
index 474933604..f872224c0 100755
--- a/rod/lookups.nim
+++ b/rod/lookups.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2009 Andreas Rumpf
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -21,28 +21,12 @@ type
     m*: PSym
     mode*: TOverloadIterMode
 
-
-proc getSymRepr*(s: PSym): string
-proc CloseScope*(tab: var TSymTab)
-proc AddSym*(t: var TStrTable, n: PSym)
-proc addDecl*(c: PContext, sym: PSym)
-proc addDeclAt*(c: PContext, sym: PSym, at: Natural)
-proc addOverloadableSymAt*(c: PContext, fn: PSym, at: Natural)
-proc addInterfaceDecl*(c: PContext, sym: PSym)
-proc addInterfaceOverloadableSymAt*(c: PContext, sym: PSym, at: int)
-proc lookUp*(c: PContext, n: PNode): PSym
-  # Looks up a symbol. Generates an error in case of nil.
-proc QualifiedLookUp*(c: PContext, n: PNode, ambiguousCheck: bool): PSym
-proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym
-proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym
-# implementation
-
-proc getSymRepr(s: PSym): string = 
+proc getSymRepr*(s: PSym): string = 
   case s.kind
   of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
   else: result = s.name.s
   
-proc CloseScope(tab: var TSymTab) = 
+proc CloseScope*(tab: var TSymTab) = 
   var 
     it: TTabIter
     s: PSym
@@ -59,14 +43,14 @@ proc CloseScope(tab: var TSymTab) =
     s = NextIter(it, tab.stack[tab.tos - 1])
   astalgo.rawCloseScope(tab)
 
-proc AddSym(t: var TStrTable, n: PSym) = 
+proc AddSym*(t: var TStrTable, n: PSym) = 
   if StrTableIncl(t, n): liMessage(n.info, errAttemptToRedefine, n.name.s)
   
-proc addDecl(c: PContext, sym: PSym) = 
+proc addDecl*(c: PContext, sym: PSym) = 
   if SymTabAddUnique(c.tab, sym) == Failure: 
     liMessage(sym.info, errAttemptToRedefine, sym.Name.s)
   
-proc addDeclAt(c: PContext, sym: PSym, at: Natural) = 
+proc addDeclAt*(c: PContext, sym: PSym, at: Natural) = 
   if SymTabAddUniqueAt(c.tab, sym, at) == Failure: 
     liMessage(sym.info, errAttemptToRedefine, sym.Name.s)
 
@@ -81,7 +65,7 @@ proc addInterfaceDeclAt*(c: PContext, sym: PSym, at: Natural) =
   addDeclAt(c, sym, at)
   AddInterfaceDeclAux(c, sym)
   
-proc addOverloadableSymAt(c: PContext, fn: PSym, at: Natural) = 
+proc addOverloadableSymAt*(c: PContext, fn: PSym, at: Natural) = 
   if not (fn.kind in OverloadableSyms): 
     InternalError(fn.info, "addOverloadableSymAt")
   var check = StrTableGet(c.tab.stack[at], fn.name)
@@ -89,17 +73,17 @@ proc addOverloadableSymAt(c: PContext, fn: PSym, at: Natural) =
     liMessage(fn.info, errAttemptToRedefine, fn.Name.s)
   SymTabAddAt(c.tab, fn, at)
   
-proc addInterfaceDecl(c: PContext, sym: PSym) = 
+proc addInterfaceDecl*(c: PContext, sym: PSym) = 
   # it adds the symbol to the interface if appropriate
   addDecl(c, sym)
   AddInterfaceDeclAux(c, sym)
 
-proc addInterfaceOverloadableSymAt(c: PContext, sym: PSym, at: int) = 
+proc addInterfaceOverloadableSymAt*(c: PContext, sym: PSym, at: int) = 
   # it adds the symbol to the interface if appropriate
   addOverloadableSymAt(c, sym, at)
   AddInterfaceDeclAux(c, sym)
 
-proc lookUp(c: PContext, n: PNode): PSym = 
+proc lookUp*(c: PContext, n: PNode): PSym = 
   # Looks up a symbol. Generates an error in case of nil.
   case n.kind
   of nkAccQuoted: 
@@ -118,26 +102,32 @@ proc lookUp(c: PContext, n: PNode): PSym =
     liMessage(n.info, errUseQualifier, result.name.s)
   if result.kind == skStub: loadStub(result)
   
-proc QualifiedLookUp(c: PContext, n: PNode, ambiguousCheck: bool): PSym = 
+type 
+  TLookupFlag* = enum 
+    checkAmbiguity, checkUndeclared
+  
+proc QualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym = 
   case n.kind
   of nkIdent: 
     result = SymtabGet(c.Tab, n.ident)
-    if result == nil: 
+    if result == nil and checkUndeclared in flags: 
       liMessage(n.info, errUndeclaredIdentifier, n.ident.s)
-    elif ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id): 
+    elif checkAmbiguity in flags and IntSetContains(c.AmbiguousSymbols, 
+                                                    result.id): 
       liMessage(n.info, errUseQualifier, n.ident.s)
   of nkSym: 
     #
-    #      result := SymtabGet(c.Tab, n.sym.name);
-    #      if result = nil then
+    #      result = SymtabGet(c.Tab, n.sym.name)
+    #      if result == nil:
     #        liMessage(n.info, errUndeclaredIdentifier, n.sym.name.s)
     #      else 
     result = n.sym
-    if ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id): 
+    if checkAmbiguity in flags and IntSetContains(c.AmbiguousSymbols, 
+                                                  result.id): 
       liMessage(n.info, errUseQualifier, n.sym.name.s)
   of nkDotExpr: 
     result = nil
-    var m = qualifiedLookUp(c, n.sons[0], false)
+    var m = qualifiedLookUp(c, n.sons[0], flags*{checkUndeclared})
     if (m != nil) and (m.kind == skModule): 
       var ident: PIdent = nil
       if (n.sons[1].kind == nkIdent): 
@@ -150,17 +140,17 @@ proc QualifiedLookUp(c: PContext, n: PNode, ambiguousCheck: bool): PSym =
           result = StrTableGet(c.tab.stack[ModuleTablePos], ident)
         else: 
           result = StrTableGet(m.tab, ident)
-        if result == nil: 
+        if result == nil and checkUndeclared in flags: 
           liMessage(n.sons[1].info, errUndeclaredIdentifier, ident.s)
-      else: 
+      elif checkUndeclared in flags: 
         liMessage(n.sons[1].info, errIdentifierExpected, renderTree(n.sons[1]))
   of nkAccQuoted: 
-    result = QualifiedLookup(c, n.sons[0], ambiguousCheck)
+    result = QualifiedLookup(c, n.sons[0], flags)
   else: 
-    result = nil              #liMessage(n.info, errIdentifierExpected, '')
+    result = nil
   if (result != nil) and (result.kind == skStub): loadStub(result)
   
-proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym = 
+proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = 
   var ident: PIdent
   result = nil
   case n.kind
@@ -173,17 +163,16 @@ proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.ident)
   of nkSym: 
     result = n.sym
-    o.mode = oimDone #
-                     #      o.stackPtr := c.tab.tos;
-                     #      o.mode := oimNoQualifier;
-                     #      while (result = nil) do begin
-                     #        dec(o.stackPtr);
-                     #        if o.stackPtr < 0 then break;
-                     #        result := InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name);
-                     #      end; 
+    o.mode = oimDone 
+    #      o.stackPtr = c.tab.tos
+    #      o.mode = oimNoQualifier
+    #      while result == nil:
+    #        dec(o.stackPtr)
+    #        if o.stackPtr < 0: break
+    #        result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name)
   of nkDotExpr: 
     o.mode = oimOtherModule
-    o.m = qualifiedLookUp(c, n.sons[0], false)
+    o.m = qualifiedLookUp(c, n.sons[0])
     if (o.m != nil) and (o.m.kind == skModule): 
       ident = nil
       if (n.sons[1].kind == nkIdent): 
@@ -210,7 +199,7 @@ proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     nil
   if (result != nil) and (result.kind == skStub): loadStub(result)
   
-proc nextOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym = 
+proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = 
   case o.mode
   of oimDone: 
     result = nil
@@ -222,8 +211,8 @@ proc nextOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       while (result == nil): 
         dec(o.stackPtr)
         if o.stackPtr < 0: break 
-        result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], o.it.name) # BUGFIX: 
-                                                                         # o.it.name <-> n.ident
+        result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], o.it.name) 
+        # BUGFIX: o.it.name <-> n.ident
     else: 
       result = nil
   of oimSelfModule: 
diff --git a/rod/options.nim b/rod/options.nim
index cdba2fcdc..1503770c3 100755
--- a/rod/options.nim
+++ b/rod/options.nim
@@ -30,7 +30,8 @@ type                          # please make sure we have under 32 options
   TOptions* = set[TOption]
   TGlobalOption* = enum 
     gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim, 
-    optListCmd, optCompileOnly, optNoLinking, optSafeCode, # only allow safe code
+    optListCmd, optCompileOnly, optNoLinking, 
+    optSafeCode,              # only allow safe code
     optCDebug,                # turn on debugging information
     optGenDynLib,             # generate a dynamic library
     optGenGuiApp,             # generate a GUI application
@@ -40,7 +41,8 @@ type                          # please make sure we have under 32 options
     optSymbolFiles,           # use symbol files for speeding up compilation
     optSkipConfigFile,        # skip the general config file
     optSkipProjConfigFile,    # skip the project's config file
-    optNoMain                 # do not generate a "main" proc
+    optNoMain,                # do not generate a "main" proc
+    optThreads                # support for multi-threading
   TGlobalOptions* = set[TGlobalOption]
   TCommands* = enum           # Nimrod's commands
     cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, 
@@ -142,7 +144,7 @@ proc shortenDir(dir: string): string =
     return copy(dir, len(prefix))
   result = dir
 
-proc removeTrailingDirSep(path: string): string = 
+proc removeTrailingDirSep*(path: string): string = 
   if (len(path) > 0) and (path[len(path) - 1] == dirSep): 
     result = copy(path, 0, len(path) - 2)
   else: 
diff --git a/rod/semexprs.nim b/rod/semexprs.nim
index e6cf097e5..cdcf09d3e 100755
--- a/rod/semexprs.nim
+++ b/rod/semexprs.nim
@@ -631,7 +631,7 @@ proc makeDeref(n: PNode): PNode =
 
 proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if it's not a built-in field access
-  var s = qualifiedLookup(c, n, true) # check for ambiguity
+  var s = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared})
   if s != nil:
     return semSym(c, n, s, flags)
 
@@ -940,7 +940,7 @@ proc semMacroStmt(c: PContext, n: PNode, semCheck: bool = true): PNode =
   var a: PNode
   if isCallExpr(n.sons[0]): a = n.sons[0].sons[0]
   else: a = n.sons[0]
-  var s = qualifiedLookup(c, a, false)
+  var s = qualifiedLookup(c, a, {checkUndeclared})
   if s != nil: 
     case s.kind
     of skMacro: 
@@ -1014,7 +1014,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: 
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
-    var s = qualifiedLookup(c, n.sons[0], false)
+    var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
     if s != nil: 
       case s.kind
       of skMacro: result = semMacroExpr(c, n, s)
@@ -1037,10 +1037,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     result = semMacroStmt(c, n)
   of nkBracketExpr: 
     checkMinSonsLen(n, 1)
-    var s = qualifiedLookup(c, n.sons[0], false)
+    var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
     if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: 
       # type parameters: partial generic specialization
-      result = partialSpecialization(c, n, s)
+      result = explictGenericInstantiation(c, n, s)
     else: 
       result = semArrayAccess(c, n, flags)
   of nkPragmaExpr: 
diff --git a/rod/semgnrc.nim b/rod/semgnrc.nim
index 0a291cd42..3ccd415f9 100755
--- a/rod/semgnrc.nim
+++ b/rod/semgnrc.nim
@@ -21,12 +21,14 @@ type
   TSemGenericFlags = set[TSemGenericFlag]
 
 proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
-proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode = 
+proc semGenericStmtScope(c: PContext, n: PNode, 
+                         flags: TSemGenericFlags = {}): PNode = 
   openScope(c.tab)
   result = semGenericStmt(c, n, flags)
   closeScope(c.tab)
 
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode = 
+  incl(s.flags, sfUsed)
   case s.kind
   of skUnknown: 
     # Introduced in this pass! Leave it as an identifier.
@@ -55,20 +57,29 @@ proc getIdentNode(n: PNode): PNode =
     illFormedAst(n)
     result = nil
 
+#  of nkAccQuoted: 
+#    s = lookUp(c, n)
+#    if withinBind in flags: result = symChoice(c, n, s)
+#    else: result = semGenericStmtSymbol(c, n, s)
+
 proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode = 
   var 
     L: int
     a: PNode
-    s: PSym
   result = n
   if n == nil: return 
   case n.kind
-  of nkIdent, nkAccQuoted: 
-    s = lookUp(c, n)
-    if withinBind in flags: result = symChoice(c, n, s)
-    else: result = semGenericStmtSymbol(c, n, s)
+  of nkIdent:
+    var s = SymtabGet(c.Tab, n.ident)
+    if s == nil:
+      # no error if symbol cannot be bound, unless in ``bind`` context:
+      if withinBind in flags: 
+        liMessage(n.info, errUndeclaredIdentifier, n.ident.s)
+    else:
+      if withinBind in flags: result = symChoice(c, n, s)
+      else: result = semGenericStmtSymbol(c, n, s)
   of nkDotExpr: 
-    s = QualifiedLookUp(c, n, true)
+    var s = QualifiedLookUp(c, n, {})
     if s != nil: result = semGenericStmtSymbol(c, n, s)
   of nkSym..nkNilLit: 
     nil
@@ -77,8 +88,9 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
   of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: 
     # check if it is an expression macro:
     checkMinSonsLen(n, 1)
-    s = qualifiedLookup(c, n.sons[0], false)
-    if (s != nil): 
+    var s = qualifiedLookup(c, n.sons[0], {})
+    if s != nil: 
+      incl(s.flags, sfUsed)
       case s.kind
       of skMacro: 
         return semMacroExpr(c, n, s, false)
@@ -100,16 +112,16 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
   of nkMacroStmt: 
     result = semMacroStmt(c, n, false)
   of nkIfStmt: 
-    for i in countup(0, sonsLen(n) - 1): 
+    for i in countup(0, sonsLen(n)-1): 
       n.sons[i] = semGenericStmtScope(c, n.sons[i])
   of nkWhileStmt: 
     openScope(c.tab)
-    for i in countup(0, sonsLen(n) - 1): n.sons[i] = semGenericStmt(c, n.sons[i])
+    for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i])
     closeScope(c.tab)
   of nkCaseStmt: 
     openScope(c.tab)
     n.sons[0] = semGenericStmt(c, n.sons[0])
-    for i in countup(1, sonsLen(n) - 1): 
+    for i in countup(1, sonsLen(n)-1): 
       a = n.sons[i]
       checkMinSonsLen(a, 1)
       L = sonsLen(a)
diff --git a/rod/seminst.nim b/rod/seminst.nim
index 417922825..7954d2e8f 100755
--- a/rod/seminst.nim
+++ b/rod/seminst.nim
@@ -106,6 +106,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   else: 
     result.typ = newTypeS(tyProc, c)
     addSon(result.typ, nil)
+  result.typ.callConv = fn.typ.callConv
   oldPrc = GenericCacheGet(c, fn, result)
   if oldPrc == nil: 
     # add it here, so that recursive generic procs are possible:
@@ -244,10 +245,3 @@ proc generateTypeInstance(p: PContext, pt: TIdTable, arg: PNode,
   result = ReplaceTypeVarsT(cl, t)
   popInfoContext()
 
-proc partialSpecialization(c: PContext, n: PNode, s: PSym): PNode = 
-  for i in 1..sonsLen(n)-1:
-    n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
-  # we cannot check for the proper number of type parameters because in
-  # `f[a,b](x, y)` `f` is not resolved yet properly.
-  # XXX: BUG this should be checked somehow!
-  result = n
diff --git a/rod/semstmts.nim b/rod/semstmts.nim
index 0546c24dd..0faf3e5fc 100755
--- a/rod/semstmts.nim
+++ b/rod/semstmts.nim
@@ -162,7 +162,7 @@ proc propertyWriteAccess(c: PContext, n, a: PNode): PNode =
   addSon(result, 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
-  # nodes!
+  # nodes?
   addSon(result, a[0])
   addSon(result, semExpr(c, n[1]))
   result = semDirectCallAnalyseEffects(c, result, {})
@@ -182,18 +182,6 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     a = builtinFieldAccess(c, a, {efLValue})
     if a == nil: 
       return propertyWriteAccess(c, n, n[0])
-    when false:
-      checkSonsLen(a, 2)
-      var id = considerAcc(a.sons[1])
-      result = newNodeI(nkCall, n.info)
-      addSon(result, newIdentNode(getIdent(id.s & '='), n.info))
-      addSon(result, semExpr(c, a.sons[0]))
-      addSon(result, semExpr(c, n.sons[1]))
-      result = semDirectCallAnalyseEffects(c, result, {})
-      if result != nil: 
-        fixAbstractType(c, result)
-        analyseIfAddressTakenInCall(c, result)
-        return 
   of nkBracketExpr: 
     # a[i..j] = x
     # --> `[..]=`(a, i, j, x)
@@ -204,7 +192,6 @@ proc semAsgn(c: PContext, n: PNode): PNode =
       return semExprNoType(c, result)
   else: 
     a = semExprWithType(c, a, {efLValue})
-  #n.sons[0] = semExprWithType(c, n.sons[0], {efLValue})
   n.sons[0] = a
   n.sons[1] = semExprWithType(c, n.sons[1])
   var le = a.typ
@@ -288,7 +275,6 @@ proc semVar(c: PContext, n: PNode): PNode =
     else: 
       def = nil
     if not typeAllowed(typ, skVar): 
-      #debug(typ);
       liMessage(a.info, errXisNoType, typeToString(typ))
     tup = skipTypes(typ, {tyGenericInst})
     if a.kind == nkVarTuple: 
diff --git a/rod/semtypes.nim b/rod/semtypes.nim
index 52766f174..0fb359170 100755
--- a/rod/semtypes.nim
+++ b/rod/semtypes.nim
@@ -178,7 +178,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
     liMessage(n.info, errXExpectsOneTypeParam, "ordinal")
   
 proc semTypeIdent(c: PContext, n: PNode): PSym = 
-  result = qualifiedLookup(c, n, true)
+  result = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared})
   if (result != nil): 
     markUsed(n, result)
     if result.kind != skType: liMessage(n.info, errTypeExpected)
diff --git a/rod/sigmatch.nim b/rod/sigmatch.nim
index 5dc9de7b0..bcb9198da 100755
--- a/rod/sigmatch.nim
+++ b/rod/sigmatch.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2010 Andreas Rumpf
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -82,11 +82,11 @@ proc cmpCandidates(a, b: TCandidate): int =
   result = a.convMatches - b.convMatches
 
 proc writeMatches(c: TCandidate) = 
-  Writeln(stdout, "exact matches: " & $(c.exactMatches))
-  Writeln(stdout, "subtype matches: " & $(c.subtypeMatches))
-  Writeln(stdout, "conv matches: " & $(c.convMatches))
-  Writeln(stdout, "intconv matches: " & $(c.intConvMatches))
-  Writeln(stdout, "generic matches: " & $(c.genericMatches))
+  Writeln(stdout, "exact matches: " & $c.exactMatches)
+  Writeln(stdout, "subtype matches: " & $c.subtypeMatches)
+  Writeln(stdout, "conv matches: " & $c.convMatches)
+  Writeln(stdout, "intconv matches: " & $c.intConvMatches)
+  Writeln(stdout, "generic matches: " & $c.genericMatches)
 
 proc getNotFoundError(c: PContext, n: PNode): string = 
   # Gives a detailed error message; this is separated from semDirectCall,
@@ -710,8 +710,7 @@ proc semDirectCallWithBinding(c: PContext, n, f: PNode, filter: TSymKinds,
   elif y.state == csMatch and cmpCandidates(x, y) == 0 and
       not sameMethodDispatcher(x.calleeSym, y.calleeSym): 
     if x.state != csMatch: 
-      InternalError(n.info, "x.state is not csMatch") #writeMatches(x);
-                                                      #writeMatches(y);
+      InternalError(n.info, "x.state is not csMatch") 
     liMessage(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
       getProcHeader(x.calleeSym), getProcHeader(y.calleeSym), 
       x.calleeSym.Name.s])
@@ -740,3 +739,20 @@ proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode =
     initialBinding = nil
   result = semDirectCallWithBinding(c, n, f, filter, initialBinding)
 
+proc explictGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = 
+  assert n.kind == nkBracketExpr
+  for i in 1..sonsLen(n)-1:
+    n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
+  # we cannot check for the proper number of type parameters because in
+  # `f[a,b](x, y)` `f` is not resolved yet properly.
+  # XXX: BUG this should be checked somehow!
+  assert n.sons[0].kind == nkSym
+  
+  var x: TCandidate
+  initCandidate(x, s, n)
+  var newInst = generateInstance(c, s, x.bindings, n.info)
+  
+  markUsed(n, s)
+  result = newSymNode(newInst)
+  result.info = n.info
+
diff --git a/rod/types.nim b/rod/types.nim
index d7b956983..d26136aa9 100755
--- a/rod/types.nim
+++ b/rod/types.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2010 Andreas Rumpf
+#        (c) Copyright 2011 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -747,7 +747,7 @@ proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool =
   result = true
   if n != nil: 
     result = typeAllowedAux(marker, n.typ, kind)
-    if not result: debug(n.typ)
+    #if not result: debug(n.typ)
     if result: 
       case n.kind
       of nkNone..nkNilLit: 
diff --git a/rod/wordrecg.nim b/rod/wordrecg.nim
index 0b5b48bb2..d4bbb8680 100755
--- a/rod/wordrecg.nim
+++ b/rod/wordrecg.nim
@@ -59,7 +59,8 @@ type
     wPretty, 
     wDoc, wGenDepend, wListDef, wCheck, wParse, wScan, wJs, wOC, 
     wRst2html, wRst2tex, wI,
-    wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit
+    wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wThreads,
+    wRecursivePath
     
   TSpecialWords* = set[TSpecialWord]
 
@@ -107,7 +108,8 @@ const
     "compiletooc",
     "pretty", "doc", "gendepend", "listdef", "check", "parse", "scan", 
     "js", "oc", "rst2html", "rst2tex", "i", 
-    "write", "putenv", "prependenv", "appendenv", "threadvar", "emit"]
+    "write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
+    "threads", "recursivepath"]
 
 proc whichKeyword*(id: PIdent): TSpecialWord
 proc whichKeyword*(id: String): TSpecialWord
diff --git a/todo.txt b/todo.txt
index 08b1865b1..51ab7d592 100755
--- a/todo.txt
+++ b/todo.txt
@@ -1,6 +1,6 @@
-- built-in serialization
 - thread support: threadvar on Windows seems broken; 
   add --deadlock_prevention:on|off switch
+- built-in serialization
 
 - ban ``nil`` from the AST. This might also fix bugs concerning macros.
 
@@ -66,7 +66,8 @@ Low priority
 - tlastmod returns wrong results on BSD (Linux, MacOS X: works)
 - nested tuple unpacking
 - fast assignment optimization for TPeg
-
+- better error messages for used keywords as identifiers
+- case statement branches should support constant sets
 
 Library
 -------
diff --git a/tools/install.tmpl b/tools/install.tmpl
index 34f77aac1..9dea6887b 100755
--- a/tools/install.tmpl
+++ b/tools/install.tmpl
@@ -47,13 +47,13 @@ if [ $# -eq 1 ] ; then
       docdir="$1/?proj/doc"
       datadir="$1/?proj/data"
       
-      mkdir -p $1/?proj
-      mkdir -p $bindir
-      mkdir -p $configdir
+      mkdir -p $1/?proj || exit 1
+      mkdir -p $bindir || exit 1
+      mkdir -p $configdir || exit 1
       ;;
   esac
-  mkdir -p $libdir
-  mkdir -p $docdir
+  mkdir -p $libdir || exit 1
+  mkdir -p $docdir || exit 1
   echo "copying files..."
 #var createdDirs = newStringTable()
 #for cat in fcConfig..fcLib:
@@ -63,30 +63,30 @@ if [ $# -eq 1 ] ; then
 #      mk = unixDirVars[cat] & "/" & mk
 #      if not createdDirs.hasKey(mk):
 #        createdDirs[mk] = "true"
-  mkdir -p ?mk
+  mkdir -p ?mk || exit 1
 #      end if
 #    end if
 #  end for
 #end for
 
 #for f in items(c.cat[fcUnixBin]):
-  cp ?f $bindir/?f.skipRoot
+  cp ?f $bindir/?f.skipRoot || exit 1
   chmod 755 $bindir/?f.skipRoot
 #end for
 #for f in items(c.cat[fcConfig]): 
-  cp ?f $configdir/?f.skipRoot
+  cp ?f $configdir/?f.skipRoot || exit 1
   chmod 644 $configdir/?f.skipRoot
 #end for
 #for f in items(c.cat[fcData]): 
-  cp ?f $datadir/?f.skipRoot
+  cp ?f $datadir/?f.skipRoot || exit 1
   chmod 644 $datadir/?f.skipRoot
 #end for
 #for f in items(c.cat[fcDoc]): 
-  cp ?f $docdir/?f.skipRoot
+  cp ?f $docdir/?f.skipRoot || exit 1
   chmod 644 $docdir/?f.skipRoot
 #end for
 #for f in items(c.cat[fcLib]): 
-  cp ?f $libdir/?f.skipRoot
+  cp ?f $libdir/?f.skipRoot || exit 1
   chmod 644 $libdir/?f.skipRoot
 #end for
   
diff --git a/web/news.txt b/web/news.txt
index e0c436a20..181ec706a 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -33,6 +33,8 @@ Changes affecting backwards compatibility
 Additions
 ---------
 
+- Added ``scgi`` module.
+- Added ``smtp`` module.
 - Added ``re.findAll``, ``pegs.findAll``.
 - Added ``os.findExe``.
 - Added ``strutils.align``, ``strutils.tokenize``, ``strutils.wordWrap``. 
@@ -40,7 +42,7 @@ Additions
 - Pegs support new built-ins: ``\letter``, ``\upper``, ``\lower``,
   ``\title``, ``\white``.
 - Pegs support the new built-in ``\skip`` operation.
-- Source code filters are now properly documented.
+- Source code filters are now documented.
 - Added ``emit`` pragma for direct code generator control.
 - Additional operations were added to the ``complex`` module. 
 - Added ``strutils.formatFloat``,  ``strutils.formatBiggestFloat``.