summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-05-14 00:59:07 +0200
committerAraq <rumpf_a@web.de>2013-05-14 00:59:07 +0200
commit635ad3b336bfcf89c3bb611156fd68f3cc953c30 (patch)
tree66decee75e670e5e30ff9bf15b05806348f63ae7 /compiler
parent9b9a18094732ec88157a11d3845d626b8970bfb9 (diff)
parentd3cf8121b7dddcea867328382113acfed845cf69 (diff)
downloadNim-635ad3b336bfcf89c3bb611156fd68f3cc953c30.tar.gz
Merge branch 'master' of github.com:Araq/Nimrod
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ast.nim25
-rw-r--r--compiler/astalgo.nim80
-rw-r--r--compiler/cgen.nim4
-rw-r--r--compiler/importer.nim8
-rw-r--r--compiler/lookups.nim162
-rw-r--r--compiler/magicsys.nim2
-rw-r--r--compiler/pragmas.nim13
-rw-r--r--compiler/procfind.nim19
-rw-r--r--compiler/sem.nim17
-rw-r--r--compiler/semcall.nim5
-rw-r--r--compiler/semdata.nim14
-rw-r--r--compiler/semdestruct.nim2
-rw-r--r--compiler/semexprs.nim34
-rw-r--r--compiler/semgnrc.nim32
-rw-r--r--compiler/seminst.nim8
-rw-r--r--compiler/semmagic.nim5
-rw-r--r--compiler/semstmts.nim130
-rw-r--r--compiler/semtempl.nim19
-rw-r--r--compiler/semtypes.nim43
-rw-r--r--compiler/sigmatch.nim25
-rw-r--r--compiler/suggest.nim19
-rw-r--r--compiler/types.nim5
-rw-r--r--compiler/wordrecg.nim4
23 files changed, 360 insertions, 315 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index eb12c977f..d4d5bce9c 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -13,10 +13,6 @@ import
   msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, 
   intsets, idgen
 
-const
-  ImportTablePos* = 0         # imported symbols are at level 0
-  ModuleTablePos* = 1         # module's top level symbols are at level 1
-
 type 
   TCallingConvention* = enum 
     ccDefault,                # proc has no explicit calling convention
@@ -284,6 +280,9 @@ const
       
   sfHoist* = sfVolatile ## proc return value can be hoisted
 
+  sfNoForward* = sfRegister
+    # forward declarations are not required (per module)
+
 const
   # getting ready for the future expr/stmt merge
   nkWhen* = nkWhenStmt
@@ -608,7 +607,14 @@ type
                               # simple ref count here
   
   PInstantiation* = ref TInstantiation
-      
+ 
+  TScope* = object
+    depthLevel*: int
+    symbols*: TStrTable
+    parent*: PScope
+
+  PScope* = ref TScope
+
   PLib* = ref TLib
   TSym* {.acyclic.} = object of TIdObj
     # proc and type instantiations are cached in the generic symbol
@@ -617,12 +623,14 @@ type
       typeInstCache*: seq[PType]
     of routineKinds:
       procInstCache*: seq[PInstantiation]
+      scope*: PScope          # the scope where the proc was defined
     of skModule:
       # modules keep track of the generic symbols they use from other modules.
       # this is because in incremental compilation, when a module is about to
       # be replaced with a newer version, we must decrement the usage count
       # of all previously used generics.
       usedGenerics*: seq[PInstantiation]
+      tab*: TStrTable         # interface table for modules
     else: nil
 
     magic*: TMagic
@@ -631,7 +639,6 @@ type
     info*: TLineInfo
     owner*: PSym
     flags*: TSymFlags
-    tab*: TStrTable           # interface table for modules
     ast*: PNode               # syntax tree of proc, iterator, etc.:
                               # the whole proc including header; this is used
                               # for easy generation of proper error messages
@@ -1053,7 +1060,8 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
     when debugIds: RegisterId(result)
   result.flags = s.flags
   result.magic = s.magic
-  copyStrTable(result.tab, s.tab)
+  if s.kind == skModule:
+    copyStrTable(result.tab, s.tab)
   result.options = s.options
   result.position = s.position
   result.loc = s.loc
@@ -1080,6 +1088,9 @@ proc initStrTable(x: var TStrTable) =
   x.counter = 0
   newSeq(x.data, startSize)
 
+proc newStrTable*: TStrTable =
+  initStrTable(result)
+
 proc initTable(x: var TTable) = 
   x.counter = 0
   newSeq(x.data, startSize)
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 364e164bb..fd6774e7a 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -72,37 +72,6 @@ type
 proc InitIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym
 proc NextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym
 
-# -------------- symbol table ----------------------------------------------
-# Each TParser object (which represents a module being compiled) has its own
-# symbol table. A symbol table is organized as a stack of str tables. The
-# stack represents the different scopes.
-# Stack pointer:
-# 0                imported symbols from other modules
-# 1                module level
-# 2                proc level
-# 3                nested statements
-# ...
-#
-type 
-  TSymTab*{.final.} = object 
-    tos*: Natural             # top of stack
-    stack*: seq[TStrTable]
-
-
-proc InitSymTab*(tab: var TSymTab)
-proc DeinitSymTab*(tab: var TSymTab)
-proc SymTabGet*(tab: TSymTab, s: PIdent): PSym
-proc SymTabGet*(tab: TSymTab, s: PIdent, filter: TSymKinds): PSym
-proc SymTabLocalGet*(tab: TSymTab, s: PIdent): PSym
-proc SymTabAdd*(tab: var TSymTab, e: PSym)
-proc SymTabAddAt*(tab: var TSymTab, e: PSym, at: Natural)
-proc SymTabAddUnique*(tab: var TSymTab, e: PSym): TResult
-proc SymTabAddUniqueAt*(tab: var TSymTab, e: PSym, at: Natural): TResult
-proc OpenScope*(tab: var TSymTab)
-proc RawCloseScope*(tab: var TSymTab)
-  # the real "closeScope" adds some
-  # checks in parsobj
-
 # these are for debugging only: They are not really deprecated, but I want
 # the warning so that release versions do not contain debugging statements:
 proc debug*(n: PSym) {.deprecated.}
@@ -708,52 +677,7 @@ proc NextIter(ti: var TTabIter, tab: TStrTable): PSym =
     result = tab.data[ti.h]
     Inc(ti.h)                 # ... and increment by one always
     if result != nil: break 
-  
-proc InitSymTab(tab: var TSymTab) = 
-  tab.tos = 0
-  tab.stack = EmptySeq
-
-proc DeinitSymTab(tab: var TSymTab) = 
-  tab.stack = nil
 
-proc SymTabLocalGet(tab: TSymTab, s: PIdent): PSym = 
-  result = StrTableGet(tab.stack[tab.tos - 1], s)
-
-proc SymTabGet(tab: TSymTab, s: PIdent): PSym = 
-  for i in countdown(tab.tos - 1, 0): 
-    result = StrTableGet(tab.stack[i], s)
-    if result != nil: return 
-  result = nil
-
-proc SymTabGet*(tab: TSymTab, s: PIdent, filter: TSymKinds): PSym =
-  for i in countdown(tab.tos - 1, 0): 
-    result = StrTableGet(tab.stack[i], s)
-    if result != nil and result.kind in filter: return
-  result = nil
-
-proc SymTabAddAt(tab: var TSymTab, e: PSym, at: Natural) = 
-  StrTableAdd(tab.stack[at], e)
-
-proc SymTabAdd(tab: var TSymTab, e: PSym) = 
-  StrTableAdd(tab.stack[tab.tos - 1], e)
-
-proc SymTabAddUniqueAt(tab: var TSymTab, e: PSym, at: Natural): TResult = 
-  if StrTableIncl(tab.stack[at], e): 
-    result = Failure
-  else: 
-    result = Success
-
-proc SymTabAddUnique(tab: var TSymTab, e: PSym): TResult = 
-  result = SymTabAddUniqueAt(tab, e, tab.tos - 1)
-
-proc OpenScope(tab: var TSymTab) = 
-  if tab.tos >= len(tab.stack): setlen(tab.stack, tab.tos + 1)
-  initStrTable(tab.stack[tab.tos])
-  Inc(tab.tos)
-
-proc RawCloseScope(tab: var TSymTab) = 
-  Dec(tab.tos)
-  
 iterator items*(tab: TStrTable): PSym = 
   var it: TTabIter
   var s = InitTabIter(it, tab)
@@ -761,10 +685,6 @@ iterator items*(tab: TStrTable): PSym =
     yield s
     s = NextIter(it, tab)
 
-iterator items*(tab: TSymTab): PSym = 
-  for i in countdown(tab.tos-1, 0): 
-    for it in items(tab.stack[i]): yield it
-
 proc hasEmptySlot(data: TIdPairSeq): bool = 
   for h in countup(0, high(data)): 
     if data[h].key == nil: 
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index f034f6675..ddb9ec0ad 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -842,8 +842,10 @@ proc requestConstImpl(p: BProc, sym: PSym) =
     if sfExportc in sym.flags and generatedHeader != nil:
       app(generatedHeader.s[cfsData], headerDecl)
 
+proc isActivated(prc: PSym): bool = prc.typ != nil
+
 proc genProc(m: BModule, prc: PSym) = 
-  if sfBorrow in prc.flags: return 
+  if sfBorrow in prc.flags or not isActivated(prc): return
   fillProcLoc(prc)
   if {sfForward, sfFromGeneric} * prc.flags != {}: addForwardedProc(m, prc)
   else: 
diff --git a/compiler/importer.nim b/compiler/importer.nim
index d274b4693..ebb848ea7 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -48,7 +48,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
   # This does not handle stubs, because otherwise loading on demand would be
   # pointless in practice. So importing stubs is fine here!
   # check if we have already a symbol of the same name:
-  var check = StrTableGet(c.tab.stack[importTablePos], s.name)
+  var check = StrTableGet(c.importTable.symbols, s.name)
   if check != nil and check.id != s.id:
     if s.kind notin OverloadableSyms:
       # s and check need to be qualified:
@@ -56,7 +56,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
       Incl(c.AmbiguousSymbols, check.id)
   # thanks to 'export' feature, it could be we import the same symbol from
   # multiple sources, so we need to call 'StrTableAdd' here:
-  StrTableAdd(c.tab.stack[importTablePos], s)
+  StrTableAdd(c.importTable.symbols, s)
   if s.kind == skType:
     var etyp = s.typ
     if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
@@ -68,12 +68,12 @@ proc rawImportSymbol(c: PContext, s: PSym) =
           # have been put into the symbol table
           # BUGFIX: but only iff they are the same symbols!
         var it: TIdentIter 
-        check = InitIdentIter(it, c.tab.stack[importTablePos], e.name)
+        check = InitIdentIter(it, c.importTable.symbols, e.name)
         while check != nil:
           if check.id == e.id:
             e = nil
             break
-          check = NextIdentIter(it, c.tab.stack[importTablePos])
+          check = NextIdentIter(it, c.importTable.symbols)
         if e != nil:
           rawImportSymbol(c, e)
   else:
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index c0e3ea880..05470f54f 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -13,6 +13,8 @@ import
   intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread, 
   renderer, wordrecg, idgen
 
+proc ensureNoMissingOrUnusedSymbols(scope: PScope)
+
 proc considerAcc*(n: PNode): PIdent = 
   case n.kind
   of nkIdent: result = n.ident
@@ -33,6 +35,49 @@ proc considerAcc*(n: PNode): PIdent =
   else:
     GlobalError(n.info, errIdentifierExpected, renderTree(n))
  
+template addSym*(scope: PScope, s: PSym) =
+  StrTableAdd(scope.symbols, s)
+
+proc addUniqueSym*(scope: PScope, s: PSym): TResult =
+  if StrTableIncl(scope.symbols, s):
+    result = Failure
+  else:
+    result = Success
+
+proc openScope*(c: PContext): PScope {.discardable.} =
+  result = PScope(parent: c.currentScope,
+                  symbols: newStrTable(),
+                  depthLevel: c.scopeDepth + 1)
+  c.currentScope = result
+
+proc rawCloseScope*(c: PContext) =
+  c.currentScope = c.currentScope.parent
+
+proc closeScope*(c: PContext) =
+  ensureNoMissingOrUnusedSymbols(c.currentScope)
+  rawCloseScope(c)
+
+iterator walkScopes*(scope: PScope): PScope =
+  var current = scope
+  while current != nil:
+    yield current
+    current = current.parent
+
+proc localSearchInScope*(c: PContext, s: PIdent): PSym =
+  result = StrTableGet(c.currentScope.symbols, s)
+
+proc searchInScopes*(c: PContext, s: PIdent): PSym =
+  for scope in walkScopes(c.currentScope):
+    result = StrTableGet(scope.symbols, s)
+    if result != nil: return
+  result = nil
+
+proc searchInScopes*(c: PContext, s: PIdent, filter: TSymKinds): PSym =
+  for scope in walkScopes(c.currentScope):
+    result = StrTableGet(scope.symbols, s)
+    if result != nil and result.kind in filter: return
+  result = nil
+
 proc errorSym*(c: PContext, n: PNode): PSym =
   ## creates an error symbol to avoid cascading errors (for IDE support)
   var m = n
@@ -46,31 +91,29 @@ proc errorSym*(c: PContext, n: PNode): PSym =
   result.typ = errorType(c)
   incl(result.flags, sfDiscardable)
   # pretend it's imported from some unknown module to prevent cascading errors:
-  SymTabAddAt(c.tab, result, ast.ImportTablePos)
+  c.importTable.addSym(result)
 
 type 
   TOverloadIterMode* = enum 
     oimDone, oimNoQualifier, oimSelfModule, oimOtherModule, oimSymChoice,
     oimSymChoiceLocalLookup
   TOverloadIter*{.final.} = object 
-    stackPtr*: int
     it*: TIdentIter
     m*: PSym
     mode*: TOverloadIterMode
+    symChoiceIndex*: int
+    scope*: PScope
     inSymChoice: TIntSet
 
 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 ensureNoMissingOrUnusedSymbols(scope: PScope) =
   # check if all symbols have been used and defined:
-  if tab.tos > len(tab.stack): 
-    InternalError("CloseScope")
-    return
   var it: TTabIter
-  var s = InitTabIter(it, tab.stack[tab.tos-1])
+  var s = InitTabIter(it, scope.symbols)
   var missingImpls = 0
   while s != nil:
     if sfForward in s.flags:
@@ -83,25 +126,21 @@ proc CloseScope*(tab: var TSymTab) =
       # BUGFIX: check options in s!
       if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
         Message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
-    s = NextIter(it, tab.stack[tab.tos-1])
-  astalgo.rawCloseScope(tab)
-
+    s = NextIter(it, scope.symbols)
+  
 proc WrongRedefinition*(info: TLineInfo, s: string) =
   if gCmd != cmdInteractive:
     localError(info, errAttemptToRedefine, s)
-
-proc AddSym*(t: var TStrTable, n: PSym) = 
-  if StrTableIncl(t, n): WrongRedefinition(n.info, n.name.s)
   
-proc addDecl*(c: PContext, sym: PSym) = 
-  if SymTabAddUnique(c.tab, sym) == Failure: 
+proc addDecl*(c: PContext, sym: PSym) =
+  if c.currentScope.addUniqueSym(sym) == Failure:
     WrongRedefinition(sym.info, sym.Name.s)
 
 proc addPrelimDecl*(c: PContext, sym: PSym) =
-  discard SymTabAddUnique(c.tab, sym)
+  discard c.currentScope.addUniqueSym(sym)
 
-proc addDeclAt*(c: PContext, sym: PSym, at: Natural) = 
-  if SymTabAddUniqueAt(c.tab, sym, at) == Failure: 
+proc addDeclAt*(scope: PScope, sym: PSym) =
+  if scope.addUniqueSym(sym) == Failure:
     WrongRedefinition(sym.info, sym.Name.s)
 
 proc AddInterfaceDeclAux(c: PContext, sym: PSym) = 
@@ -110,35 +149,35 @@ proc AddInterfaceDeclAux(c: PContext, sym: PSym) =
     if c.module != nil: StrTableAdd(c.module.tab, sym)
     else: InternalError(sym.info, "AddInterfaceDeclAux")
 
-proc addInterfaceDeclAt*(c: PContext, sym: PSym, at: Natural) = 
-  addDeclAt(c, sym, at)
+proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
+  addDeclAt(scope, sym)
   AddInterfaceDeclAux(c, sym)
-  
-proc addOverloadableSymAt*(c: PContext, fn: PSym, at: Natural) = 
+
+proc addOverloadableSymAt*(scope: PScope, fn: PSym) =
   if fn.kind notin OverloadableSyms: 
     InternalError(fn.info, "addOverloadableSymAt")
     return
-  var check = StrTableGet(c.tab.stack[at], fn.name)
+  var check = StrTableGet(scope.symbols, fn.name)
   if check != nil and check.Kind notin OverloadableSyms: 
     WrongRedefinition(fn.info, fn.Name.s)
   else:
-    SymTabAddAt(c.tab, fn, at)
+    scope.addSym(fn)
   
 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, scope: PScope, sym: PSym) =
   # it adds the symbol to the interface if appropriate
-  addOverloadableSymAt(c, sym, at)
+  addOverloadableSymAt(scope, sym)
   AddInterfaceDeclAux(c, sym)
 
 proc lookUp*(c: PContext, n: PNode): PSym = 
   # Looks up a symbol. Generates an error in case of nil.
   case n.kind
   of nkIdent:
-    result = SymtabGet(c.Tab, n.ident)
+    result = searchInScopes(c, n.ident)
     if result == nil: 
       LocalError(n.info, errUndeclaredIdentifier, n.ident.s)
       result = errorSym(c, n)
@@ -146,7 +185,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
     result = n.sym
   of nkAccQuoted:
     var ident = considerAcc(n)
-    result = SymtabGet(c.Tab, ident)
+    result = searchInScopes(c, ident)
     if result == nil:
       LocalError(n.info, errUndeclaredIdentifier, ident.s)
       result = errorSym(c, n)
@@ -165,7 +204,7 @@ proc QualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
   case n.kind
   of nkIdent, nkAccQuoted:
     var ident = considerAcc(n)
-    result = SymtabGet(c.Tab, ident)
+    result = searchInScopes(c, ident)
     if result == nil and checkUndeclared in flags: 
       LocalError(n.info, errUndeclaredIdentifier, ident.s)
       result = errorSym(c, n)
@@ -187,7 +226,7 @@ proc QualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
         ident = considerAcc(n.sons[1])
       if ident != nil: 
         if m == c.module: 
-          result = StrTableGet(c.tab.stack[ModuleTablePos], ident)
+          result = StrTableGet(c.topLevelScope.symbols, ident)
         else: 
           result = StrTableGet(m.tab, ident)
         if result == nil and checkUndeclared in flags: 
@@ -205,12 +244,15 @@ proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   case n.kind
   of nkIdent, nkAccQuoted:
     var ident = considerAcc(n)
-    o.stackPtr = c.tab.tos
+    o.scope = c.currentScope
     o.mode = oimNoQualifier
-    while result == nil:
-      dec(o.stackPtr)
-      if o.stackPtr < 0: break
-      result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], ident)
+    while true:
+      result = InitIdentIter(o.it, o.scope.symbols, ident)
+      if result != nil:
+        break
+      else:
+        o.scope = o.scope.parent
+        if o.scope == nil: break
   of nkSym:
     result = n.sym
     o.mode = oimDone
@@ -226,7 +268,7 @@ proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       if ident != nil: 
         if o.m == c.module: 
           # a module may access its private members:
-          result = InitIdentIter(o.it, c.tab.stack[ModuleTablePos], ident)
+          result = InitIdentIter(o.it, c.topLevelScope.symbols, ident)
           o.mode = oimSelfModule
         else: 
           result = InitIdentIter(o.it, o.m.tab, ident)
@@ -237,7 +279,7 @@ proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   of nkClosedSymChoice, nkOpenSymChoice:
     o.mode = oimSymChoice
     result = n.sons[0].sym
-    o.stackPtr = 1
+    o.symChoiceIndex = 1
     o.inSymChoice = initIntSet()
     Incl(o.inSymChoice, result.id)
   else: nil
@@ -245,9 +287,9 @@ proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
 
 proc lastOverloadScope*(o: TOverloadIter): int =
   case o.mode
-  of oimNoQualifier: result = o.stackPtr
-  of oimSelfModule:  result = ModuleTablePos
-  of oimOtherModule: result = ImportTablePos
+  of oimNoQualifier: result = o.scope.depthLevel
+  of oimSelfModule:  result = 1
+  of oimOtherModule: result = 0
   else: result = -1
   
 proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = 
@@ -255,41 +297,41 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   of oimDone: 
     result = nil
   of oimNoQualifier: 
-    if o.stackPtr >= 0: 
-      result = nextIdentIter(o.it, c.tab.stack[o.stackPtr])
-      while result == nil: 
-        dec(o.stackPtr)
-        if o.stackPtr < 0: break 
-        result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], o.it.name) 
+    if o.scope != nil:
+      result = nextIdentIter(o.it, o.scope.symbols)
+      while result == nil:
+        o.scope = o.scope.parent
+        if o.scope == nil: break
+        result = InitIdentIter(o.it, o.scope.symbols, o.it.name)
         # BUGFIX: o.it.name <-> n.ident
     else: 
       result = nil
   of oimSelfModule: 
-    result = nextIdentIter(o.it, c.tab.stack[ModuleTablePos])
+    result = nextIdentIter(o.it, c.topLevelScope.symbols)
   of oimOtherModule: 
     result = nextIdentIter(o.it, o.m.tab)
   of oimSymChoice: 
-    if o.stackPtr < sonsLen(n): 
-      result = n.sons[o.stackPtr].sym
+    if o.symChoiceIndex < sonsLen(n):
+      result = n.sons[o.symChoiceIndex].sym
       Incl(o.inSymChoice, result.id)
-      inc(o.stackPtr)
+      inc o.symChoiceIndex
     elif n.kind == nkOpenSymChoice:
       # try 'local' symbols too for Koenig's lookup:
       o.mode = oimSymChoiceLocalLookup
-      o.stackPtr = c.tab.tos-1
-      result = FirstIdentExcluding(o.it, c.tab.stack[o.stackPtr], 
+      o.scope = c.currentScope
+      result = FirstIdentExcluding(o.it, o.scope.symbols,
                                    n.sons[0].sym.name, o.inSymChoice)
       while result == nil:
-        dec(o.stackPtr)
-        if o.stackPtr < 0: break 
-        result = FirstIdentExcluding(o.it, c.tab.stack[o.stackPtr], 
+        o.scope = o.scope.parent
+        if o.scope == nil: break
+        result = FirstIdentExcluding(o.it, o.scope.symbols,
                                      n.sons[0].sym.name, o.inSymChoice)
   of oimSymChoiceLocalLookup:
-    result = nextIdentExcluding(o.it, c.tab.stack[o.stackPtr], o.inSymChoice)
+    result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice)
     while result == nil:
-      dec(o.stackPtr)
-      if o.stackPtr < 0: break 
-      result = FirstIdentExcluding(o.it, c.tab.stack[o.stackPtr], 
+      o.scope = o.scope.parent
+      if o.scope == nil: break
+      result = FirstIdentExcluding(o.it, o.scope.symbols,
                                    n.sons[0].sym.name, o.inSymChoice)
   
   if result != nil and result.kind == skStub: loadStub(result)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index 005ee3ae3..352b6ca04 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -19,7 +19,6 @@ proc registerSysType*(t: PType)
 proc getSysType*(kind: TTypeKind): PType
 proc getCompilerProc*(name: string): PSym
 proc registerCompilerProc*(s: PSym)
-proc InitSystem*(tab: var TSymTab)
 proc FinishSystem*(tab: TStrTable)
 proc getSysSym*(name: string): PSym
 # implementation
@@ -154,7 +153,6 @@ proc getCompilerProc(name: string): PSym =
 proc registerCompilerProc(s: PSym) = 
   strTableAdd(compilerprocs, s)
 
-proc InitSystem(tab: var TSymTab) = nil
 proc FinishSystem(tab: TStrTable) = nil
   
 initStrTable(compilerprocs)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index ba761739a..4e2a4e536 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -12,7 +12,7 @@
 import 
   os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer, 
   wordrecg, ropes, options, strutils, lists, extccomp, math, magicsys, trees,
-  rodread, types
+  rodread, types, lookups
 
 const 
   FirstCallConv* = wNimcall
@@ -42,7 +42,7 @@ const
     wFatal, wDefine, wUndef, wCompile, wLink, wLinkSys, wPure, wPush, wPop,
     wBreakpoint, wWatchpoint, wPassL, wPassC, wDeadCodeElim, wDeprecated,
     wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
-    wLinearScanEnd, wPatterns, wEffects}
+    wLinearScanEnd, wPatterns, wEffects, wNoForward}
   lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, 
     wNosideEffect, wSideEffect, wNoreturn, wDynLib, wHeader, 
     wDeprecated, wExtern, wThread, wImportcpp, wImportobjc, wNoStackFrame,
@@ -182,7 +182,11 @@ proc onOff(c: PContext, n: PNode, op: TOptions) =
 proc pragmaDeadCodeElim(c: PContext, n: PNode) = 
   if IsTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim)
   else: excl(c.module.flags, sfDeadCodeElim)
-  
+
+proc pragmaNoForward(c: PContext, n: PNode) =
+  if IsTurnedOn(c, n): incl(c.module.flags, sfNoForward)
+  else: excl(c.module.flags, sfNoForward)
+
 proc processCallConv(c: PContext, n: PNode) = 
   if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): 
     var sw = whichKeyword(n.sons[1].ident)
@@ -409,7 +413,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
       if c < 0: sub = substr(str, b + 1)
       else: sub = substr(str, b + 1, c - 1)
       if sub != "": 
-        var e = SymtabGet(con.tab, getIdent(sub))
+        var e = searchInScopes(con, getIdent(sub))
         if e != nil: 
           if e.kind == skStub: loadStub(e)
           addSon(result, newSymNode(e))
@@ -552,6 +556,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
           noVal(it)
           incl(sym.flags, sfThread)
         of wDeadCodeElim: pragmaDeadCodeElim(c, it)
+        of wNoForward: pragmaNoForward(c, it)
         of wMagic: processMagic(c, it, sym)
         of wCompileTime: 
           noVal(it)
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 2db6247e1..aefccd140 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -30,11 +30,12 @@ proc equalGenericParams(procA, procB: PNode): bool =
       if not ExprStructuralEquivalent(a.ast, b.ast): return
   result = true
 
-proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym = 
-  # Searchs for the fn in the symbol table. If the parameter lists are exactly
+proc SearchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
+  # Searchs for a forward declaration or a "twin" symbol of fn
+  # in the symbol table. If the parameter lists are exactly
   # the same the sym in the symbol table is returned, else nil.
   var it: TIdentIter
-  result = initIdentIter(it, c.tab.stack[tos], fn.Name)
+  result = initIdentIter(it, scope.symbols, fn.Name)
   if isGenericRoutine(fn):
     # we simply check the AST; this is imprecise but nearly the best what
     # can be done; this doesn't work either though as type constraints are
@@ -48,7 +49,7 @@ proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym =
                                     fn.ast.sons[paramsPos]) and
            equalGenericParams(genR, genF):
             return
-      result = NextIdentIter(it, c.tab.stack[tos])
+      result = NextIdentIter(it, scope.symbols)
   else:
     while result != nil:
       if result.Kind == fn.kind and not isGenericRoutine(result):
@@ -60,7 +61,7 @@ proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym =
           return
         of paramsNotEqual:
           nil
-      result = NextIdentIter(it, c.tab.stack[tos])
+      result = NextIdentIter(it, scope.symbols)
 
 when false:
   proc paramsFitBorrow(child, parent: PNode): bool = 
@@ -76,16 +77,16 @@ when false:
                           dcEqOrDistinctOf): return
       result = true
 
-  proc SearchForBorrowProc*(c: PContext, fn: PSym, tos: int): PSym = 
+  proc SearchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym =
     # Searchs for the fn in the symbol table. If the parameter lists are suitable
     # for borrowing the sym in the symbol table is returned, else nil.
     var it: TIdentIter
-    for scope in countdown(tos, 0): 
-      result = initIdentIter(it, c.tab.stack[scope], fn.Name)
+    for scope in walkScopes(startScope):
+      result = initIdentIter(it, scope.symbols, fn.Name)
       while result != nil: 
         # watchout! result must not be the same as fn!
         if (result.Kind == fn.kind) and (result.id != fn.id): 
           if equalGenericParams(result.ast.sons[genericParamsPos], 
                                 fn.ast.sons[genericParamsPos]): 
             if paramsFitBorrow(fn.typ.n, result.typ.n): return 
-        result = NextIdentIter(it, c.tab.stack[scope])
+        result = NextIdentIter(it, scope.symbols)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 92b25b1ba..671bd0043 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -99,7 +99,7 @@ proc commonType*(x, y: PType): PType =
         result.addSonSkipIntLit(r)
 
 proc isTopLevel(c: PContext): bool {.inline.} = 
-  result = c.tab.tos <= 2
+  result = c.currentScope.depthLevel <= 2
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = 
   result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
@@ -247,15 +247,14 @@ proc myOpen(module: PSym): PPassContext =
   c.semTypeNode = semTypeNode
   pushProcCon(c, module)
   pushOwner(c.module)
-  openScope(c.tab)            # scope for imported symbols
-  SymTabAdd(c.tab, module)    # a module knows itself
+  c.importTable = openScope(c)
+  c.importTable.addSym(module) # a module knows itself
   if sfSystemModule in module.flags: 
     magicsys.SystemModule = module # set global variable!
-    InitSystem(c.tab)         # currently does nothing
   else: 
-    SymTabAdd(c.tab, magicsys.SystemModule) # import the "System" identifier
+    c.importTable.addSym magicsys.SystemModule # import the "System" identifier
     importAllSymbols(c, magicsys.SystemModule)
-  openScope(c.tab)            # scope for the module's symbols  
+  c.topLevelScope = openScope(c)
   result = c
 
 proc myOpenCached(module: PSym, rd: PRodReader): PPassContext =
@@ -281,7 +280,7 @@ proc RecoverContext(c: PContext) =
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
   # faster than wrapping every stack operation in a 'try finally' block and 
   # requires far less code.
-  while c.tab.tos-1 > ModuleTablePos: rawCloseScope(c.tab)
+  c.currentScope = c.topLevelScope
   while getCurrOwner().kind != skModule: popOwner()
   while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
 
@@ -310,8 +309,8 @@ proc checkThreads(c: PContext) =
   
 proc myClose(context: PPassContext, n: PNode): PNode = 
   var c = PContext(context)
-  closeScope(c.tab)         # close module's scope
-  rawCloseScope(c.tab)      # imported symbols; don't check for unused ones!
+  closeScope(c)         # close module's scope
+  rawCloseScope(c)      # imported symbols; don't check for unused ones!
   result = newNode(nkStmtList)
   if n != nil:
     InternalError(n.info, "n is not nil") #result := n;
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index be41299ad..735e6fac8 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -32,6 +32,8 @@ proc sameMethodDispatcher(a, b: PSym): bool =
       # be disambiguated by the programmer; this way the right generic is
       # instantiated.
   
+proc determineType(c: PContext, s: PSym)
+
 proc resolveOverloads(c: PContext, n, orig: PNode, 
                       filter: TSymKinds): TCandidate =
   var initialBinding: PNode
@@ -58,6 +60,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
 
   while sym != nil:
     if sym.kind in filter:
+      determineType(c, sym)
       initCandidate(z, sym, initialBinding, o.lastOverloadScope)
       z.calleeSym = sym
       matches(c, n, orig, z)
@@ -198,7 +201,7 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   else:
     result = explicitGenericInstError(n)
 
-proc SearchForBorrowProc(c: PContext, fn: PSym, tos: int): PSym =
+proc SearchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
   # Searchs for the fn in the symbol table. If the parameter lists are suitable
   # for borrowing the sym in the symbol table is returned, else nil.
   # New approach: generate fn(x, y, z) where x, y, z have the proper types
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index afce365f9..4c94d0812 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -44,10 +44,13 @@ type
     efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
     efAllowDestructor
   TExprFlags* = set[TExprFlag]
-    
+
   PContext* = ref TContext
   TContext* = object of TPassContext # a context represents a module
     module*: PSym              # the module sym belonging to the context
+    currentScope*: PScope      # current scope
+    importTable*: PScope       # scope for all imported symbols
+    topLevelScope*: PScope     # scope for all top-level symbols
     p*: PProcCon               # procedure context
     friendModule*: PSym        # current friend module; may access private data;
                                # this is used so that generic instantiations
@@ -55,7 +58,6 @@ type
     InstCounter*: int          # to prevent endless instantiations
    
     threadEntries*: TSymSeq    # list of thread entries to check
-    tab*: TSymTab              # each module has its own symbol table
     AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot
                                # store this info in the syms themselves!)
     InGenericContext*: int     # > 0 if we are in a generic type
@@ -82,8 +84,7 @@ type
                                # naming it multiple times
     generics*: seq[TInstantiationPair] # pending list of instantiated generics to compile
     lastGenericIdx*: int      # used for the generics stack
-    
-
+   
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
   result.genericSym = s
   result.inst = inst
@@ -103,6 +104,10 @@ proc makeVarType*(c: PContext, baseType: PType): PType
 proc newTypeS*(kind: TTypeKind, c: PContext): PType
 proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext)
 
+proc scopeDepth*(c: PContext): int {.inline.} =
+  result = if c.currentScope != nil: c.currentScope.depthLevel
+           else: 0
+
 # owner handling:
 proc getCurrOwner*(): PSym
 proc PushOwner*(owner: PSym)
@@ -151,7 +156,6 @@ proc newOptionEntry(): POptionEntry =
 
 proc newContext(module: PSym): PContext =
   new(result)
-  InitSymTab(result.tab)
   result.AmbiguousSymbols = initIntset()
   initLinkedList(result.optionStack)
   initLinkedList(result.libs)
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index 2383ac649..797d8895e 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -120,7 +120,7 @@ proc instantiateDestructor(c: PContext, typ: PType): bool =
   of tySequence, tyArray, tyArrayConstr, tyOpenArray, tyVarargs:
     if instantiateDestructor(c, t.sons[0]):
       if rangeDestructorProc == nil:
-        rangeDestructorProc = SymtabGet(c.tab, getIdent"nimDestroyRange")
+        rangeDestructorProc = searchInScopes(c, getIdent"nimDestroyRange")
       t.destructor = rangeDestructorProc
       return true
     else:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 93dfc2f37..4f5c4fce5 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -328,9 +328,9 @@ proc semOpAux(c: PContext, n: PNode) =
 proc overloadedCallOpr(c: PContext, n: PNode): PNode = 
   # quick check if there is *any* () operator overloaded:
   var par = getIdent("()")
-  if SymtabGet(c.Tab, par) == nil: 
+  if searchInScopes(c, par) == nil:
     result = nil
-  else: 
+  else:
     result = newNodeI(nkCall, n.info)
     addSon(result, newIdentNode(par, n.info))
     for i in countup(0, sonsLen(n) - 1): addSon(result, n.sons[i])
@@ -937,7 +937,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       addSon(result, copyTree(n[0]))
     else:
       var i = considerAcc(n.sons[1])
-      var f = SymTabGet(c.tab, i)
+      var f = searchInScopes(c, i)
       # if f != nil and f.kind == skStub: loadStub(f)
       # ``loadStub`` is not correct here as we don't care for ``f`` really
       if f != nil: 
@@ -1139,7 +1139,7 @@ proc SemReturn(c: PContext, n: PNode): PNode =
     LocalError(n.info, errXNotAllowedHere, "\'return\'")
 
 proc semProcBody(c: PContext, n: PNode): PNode =
-  openScope(c.tab)
+  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
@@ -1163,7 +1163,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
       result = semAsgn(c, a)
   else:
     discardCheck(result)
-  closeScope(c.tab)
+  closeScope(c)
 
 proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) =
   var t = skipTypes(restype, {tyGenericInst})
@@ -1205,9 +1205,9 @@ proc SemYield(c: PContext, n: PNode): PNode =
 
 proc lookUpForDefined(c: PContext, i: PIdent, onlyCurrentScope: bool): PSym =
   if onlyCurrentScope: 
-    result = SymtabLocalGet(c.tab, i)
+    result = localSearchInScope(c, i)
   else: 
-    result = SymtabGet(c.Tab, i) # no need for stub loading
+    result = searchInScopes(c, i) # no need for stub loading
 
 proc LookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = 
   case n.kind
@@ -1222,7 +1222,7 @@ proc LookUpForDefined(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
       if (n.sons[1].kind == nkIdent): 
         var ident = n.sons[1].ident
         if m == c.module: 
-          result = StrTableGet(c.tab.stack[ModuleTablePos], ident)
+          result = StrTableGet(c.topLevelScope.symbols, ident)
         else: 
           result = StrTableGet(m.tab, ident)
       else: 
@@ -1374,8 +1374,8 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   msgs.gErrorMax = high(int)
   
   # open a scope for temporary symbol inclusions:
-  let oldTos = c.tab.tos
-  openScope(c.tab)
+  let oldScope = c.currentScope
+  openScope(c)
   let oldOwnerLen = len(gOwners)
   let oldGenerics = c.generics
   let oldContextLen = msgs.getInfoContextLen()
@@ -1398,7 +1398,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   c.p = oldProcCon
   msgs.setInfoContextLen(oldContextLen)
   setlen(gOwners, oldOwnerLen)
-  while c.tab.tos > oldTos: rawCloseScope(c.tab)
+  c.currentScope = oldScope
   dec c.InCompilesContext
   dec msgs.gSilence
   msgs.gErrorCounter = oldErrorCount
@@ -1641,7 +1641,7 @@ proc semBlock(c: PContext, n: PNode): PNode =
   result = n
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
-  openScope(c.tab)            # BUGFIX: label is in the scope of block!
+  openScope(c) # BUGFIX: label is in the scope of block!
   if n.sons[0].kind != nkEmpty:
     var labl = newSymG(skLabel, n.sons[0], c)
     if sfGenSym notin labl.flags:
@@ -1652,7 +1652,7 @@ proc semBlock(c: PContext, n: PNode): PNode =
   n.typ = n.sons[1].typ
   if isEmptyType(n.typ): n.kind = nkBlockStmt
   else: n.kind = nkBlockExpr
-  closeScope(c.tab)
+  closeScope(c)
   Dec(c.p.nestedBlockCounter)
 
 proc buildCall(n: PNode): PNode =
@@ -1671,11 +1671,17 @@ proc buildCall(n: PNode): PNode =
   else:
     result = n
 
+proc doBlockIsStmtList(n: PNode): bool =
+  result = n.kind == nkDo and
+           n[paramsPos].sonsLen == 1 and
+           n[paramsPos][0].kind == nkEmpty
+
 proc fixImmediateParams(n: PNode): PNode =
   # XXX: Temporary work-around until we carry out
   # the planned overload resolution reforms
   for i in 1 .. <safeLen(n):
-    if n[i].kind == nkDo: n.sons[i] = n[i][bodyPos]
+    if doBlockIsStmtList(n[i]):
+      n.sons[i] = n[i][bodyPos]
   result = n
 
 proc semExport(c: PContext, n: PNode): PNode =
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index 90e272030..abee5de5a 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -36,9 +36,9 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags,
 proc semGenericStmtScope(c: PContext, n: PNode, 
                          flags: TSemGenericFlags,
                          ctx: var TIntSet): PNode = 
-  openScope(c.tab)
+  openScope(c)
   result = semGenericStmt(c, n, flags, ctx)
-  closeScope(c.tab)
+  closeScope(c)
 
 template macroToExpand(s: expr): expr =
   s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfImmediate in s.flags)
@@ -77,7 +77,7 @@ proc Lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
             ctx: var TIntSet): PNode =
   result = n
   let ident = considerAcc(n)
-  var s = SymtabGet(c.Tab, ident)
+  var s = searchInScopes(c, ident)
   if s == nil:
     if ident.id notin ctx and withinMixin notin flags:
       localError(n.info, errUndeclaredIdentifier, ident.s)
@@ -175,12 +175,12 @@ proc semGenericStmt(c: PContext, n: PNode,
     for i in countup(0, sonsLen(n)-1):
       n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx)
   of nkWhileStmt: 
-    openScope(c.tab)
+    openScope(c)
     for i in countup(0, sonsLen(n)-1): 
       n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx)
-    closeScope(c.tab)
+    closeScope(c)
   of nkCaseStmt: 
-    openScope(c.tab)
+    openScope(c)
     n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
     for i in countup(1, sonsLen(n)-1): 
       var a = n.sons[i]
@@ -189,22 +189,22 @@ proc semGenericStmt(c: PContext, n: PNode,
       for j in countup(0, L-2): 
         a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
       a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx)
-    closeScope(c.tab)
+    closeScope(c)
   of nkForStmt, nkParForStmt: 
     var L = sonsLen(n)
-    openScope(c.tab)
+    openScope(c)
     n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
     for i in countup(0, L - 3):
       addPrelimDecl(c, newSymS(skUnknown, n.sons[i], c))
     n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
-    closeScope(c.tab)
+    closeScope(c)
   of nkBlockStmt, nkBlockExpr, nkBlockType: 
     checkSonsLen(n, 2)
-    openScope(c.tab)
+    openScope(c)
     if n.sons[0].kind != nkEmpty: 
       addPrelimDecl(c, newSymS(skUnknown, n.sons[0], c))
     n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
-    closeScope(c.tab)
+    closeScope(c)
   of nkTryStmt: 
     checkMinSonsLen(n, 2)
     n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
@@ -260,10 +260,10 @@ proc semGenericStmt(c: PContext, n: PNode,
       if (a.kind != nkTypeDef): IllFormedAst(a)
       checkSonsLen(a, 3)
       if a.sons[1].kind != nkEmpty: 
-        openScope(c.tab)
+        openScope(c)
         a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
-        closeScope(c.tab)
+        closeScope(c)
       else: 
         a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx)
   of nkEnumTy: 
@@ -276,7 +276,7 @@ proc semGenericStmt(c: PContext, n: PNode,
         of nkEnumFieldDef: a = n.sons[i].sons[0]
         of nkIdent: a = n.sons[i]
         else: illFormedAst(n)
-        addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1)
+        addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c))
   of nkObjectTy, nkTupleTy: 
     nil
   of nkFormalParams: 
@@ -298,7 +298,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     checkSonsLen(n, bodyPos + 1)
     if n.kind notin nkLambdaKinds:
       addPrelimDecl(c, newSymS(skUnknown, getIdentNode(n.sons[0]), c))
-    openScope(c.tab)
+    openScope(c)
     n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], 
                                               flags, ctx)
     if n.sons[paramsPos].kind != nkEmpty: 
@@ -310,7 +310,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     if n.sons[namePos].kind == nkSym: body = n.sons[namePos].sym.getBody
     else: body = n.sons[bodyPos]
     n.sons[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
-    closeScope(c.tab)
+    closeScope(c)
   of nkPragma, nkPragmaExpr: nil
   of nkExprColonExpr:
     checkMinSonsLen(n, 2)
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 95a394a09..9dc99d173 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -106,13 +106,13 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
     if c.generics[i].genericSym.id == s.id:
       var oldPrc = c.generics[i].inst.sym
       pushInfoContext(oldPrc.info)
-      openScope(c.tab)
+      openScope(c)
       var n = oldPrc.ast
       n.sons[bodyPos] = copyTree(s.getBody)
       if n.sons[paramsPos].kind != nkEmpty: 
         addParams(c, oldPrc.typ.n, oldPrc.kind)
       instantiateBody(c, n, oldPrc)
-      closeScope(c.tab)
+      closeScope(c)
       popInfoContext()
 
 proc sideEffectsCheck(c: PContext, s: PSym) = 
@@ -144,7 +144,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     result.owner = getCurrOwner().owner
   result.ast = n
   pushOwner(result)
-  openScope(c.tab)
+  openScope(c)
   if n.sons[genericParamsPos].kind == nkEmpty: 
     InternalError(n.info, "generateInstance")
   n.sons[namePos] = newSymNode(result)
@@ -177,7 +177,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   else:
     result = oldPrc
   popInfoContext()
-  closeScope(c.tab)           # close scope for parameters
+  closeScope(c)           # close scope for parameters
   popOwner()
   c.friendModule = oldFriend
   dec(c.InstCounter)
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 058964fc8..41c379133 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -80,8 +80,9 @@ proc semLocals(c: PContext, n: PNode): PNode =
   result = newNodeIT(nkPar, n.info, tupleType)
   tupleType.n = newNodeI(nkRecList, n.info)
   # for now we skip openarrays ...
-  for i in countdown(c.tab.tos-1, ModuleTablePos+1):
-    for it in items(c.tab.stack[i]):
+  for scope in walkScopes(c.currentScope):
+    if scope == c.topLevelScope: break
+    for it in items(scope.symbols):
       # XXX parameters' owners are wrong for generics; this caused some pain
       # for closures too; we should finally fix it.
       #if it.owner != c.p.owner: return result
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f65355069..33bc989d3 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -52,12 +52,12 @@ proc semAsm(con: PContext, n: PNode): PNode =
 proc semWhile(c: PContext, n: PNode): PNode = 
   result = n
   checkSonsLen(n, 2)
-  openScope(c.tab)
+  openScope(c)
   n.sons[0] = forceBool(c, semExprWithType(c, n.sons[0]))
   inc(c.p.nestedLoopCounter)
   n.sons[1] = semStmt(c, n.sons[1])
   dec(c.p.nestedLoopCounter)
-  closeScope(c.tab)
+  closeScope(c)
   if n.sons[1].typ == EnforceVoidContext:
     result.typ = EnforceVoidContext
 
@@ -103,9 +103,9 @@ proc semExprBranch(c: PContext, n: PNode): PNode =
     semDestructorCheck(c, result, {})
 
 proc semExprBranchScope(c: PContext, n: PNode): PNode =
-  openScope(c.tab)
+  openScope(c)
   result = semExprBranch(c, n)
-  closeScope(c.tab)
+  closeScope(c)
 
 const
   skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
@@ -150,12 +150,12 @@ proc semIf(c: PContext, n: PNode): PNode =
   for i in countup(0, sonsLen(n) - 1): 
     var it = n.sons[i]
     if it.len == 2:
-      when newScopeForIf: openScope(c.tab)
+      when newScopeForIf: openScope(c)
       it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
-      when not newScopeForIf: openScope(c.tab)
+      when not newScopeForIf: openScope(c)
       it.sons[1] = semExprBranch(c, it.sons[1])
       typ = commonType(typ, it.sons[1].typ)
-      closeScope(c.tab)
+      closeScope(c)
     elif it.len == 1:
       hasElse = true
       it.sons[0] = semExprBranchScope(c, it.sons[0])
@@ -176,7 +176,7 @@ proc semIf(c: PContext, n: PNode): PNode =
 proc semCase(c: PContext, n: PNode): PNode =
   result = n
   checkMinSonsLen(n, 2)
-  openScope(c.tab)
+  openScope(c)
   n.sons[0] = semExprWithType(c, n.sons[0])
   var chckCovered = false
   var covered: biggestint = 0
@@ -202,12 +202,12 @@ proc semCase(c: PContext, n: PNode): PNode =
     of nkElifBranch:
       chckCovered = false
       checkSonsLen(x, 2)
-      when newScopeForIf: openScope(c.tab)
+      when newScopeForIf: openScope(c)
       x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
-      when not newScopeForIf: openScope(c.tab)
+      when not newScopeForIf: openScope(c)
       x.sons[1] = semExprBranch(c, x.sons[1])
       typ = commonType(typ, x.sons[1].typ)
-      closeScope(c.tab)
+      closeScope(c)
     of nkElse:
       chckCovered = false
       checkSonsLen(x, 1)
@@ -221,7 +221,7 @@ proc semCase(c: PContext, n: PNode): PNode =
       hasElse = true
     else:
       localError(n.info, errNotAllCasesCovered)
-  closeScope(c.tab)
+  closeScope(c)
   if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
     for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
     # propagate any enforced VoidContext:
@@ -289,8 +289,9 @@ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode =
     changeType(result, typ, check=false)
 
 proc findShadowedVar(c: PContext, v: PSym): PSym =
-  for i in countdown(c.tab.tos - 2, ModuleTablePos+1):
-    let shadowed = StrTableGet(c.tab.stack[i], v.name)
+  for scope in walkScopes(c.currentScope.parent):
+    if scope == c.topLevelScope: break
+    let shadowed = StrTableGet(scope.symbols, v.name)
     if shadowed != nil and shadowed.kind in skLocalVars:
       return shadowed
 
@@ -472,12 +473,12 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
     var fc: TFieldInstCtx  # either 'tup[i]' or 'field' is valid
     fc.field = typ.sym
     fc.replaceByFieldName = c.m == mFieldPairs
-    openScope(c.c.tab)
+    openScope(c.c)
     inc c.c.InUnrolledContext
     let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
     father.add(SemStmt(c.c, body))
     dec c.c.InUnrolledContext
-    closeScope(c.c.tab)
+    closeScope(c.c)
   of nkNilLit: nil
   of nkRecCase:
     let L = forLoop.len
@@ -541,7 +542,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   if tupleTypeA.kind == tyTuple:
     var loopBody = n.sons[length-1]
     for i in 0..sonsLen(tupleTypeA)-1:
-      openScope(c.tab)
+      openScope(c)
       var fc: TFieldInstCtx
       fc.tupleType = tupleTypeA
       fc.tupleIndex = i
@@ -550,7 +551,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
       inc c.InUnrolledContext
       stmts.add(SemStmt(c, body))
       dec c.InUnrolledContext
-      closeScope(c.tab)
+      closeScope(c)
   else:
     var fc: TFieldsCtx
     fc.m = m
@@ -618,7 +619,7 @@ proc semFor(c: PContext, n: PNode): PNode =
   result = n
   checkMinSonsLen(n, 3)
   var length = sonsLen(n)
-  openScope(c.tab)
+  openScope(c)
   n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
   var call = n.sons[length-2]
   if call.kind in nkCallKinds and call.sons[0].typ.callConv == ccClosure:
@@ -644,7 +645,7 @@ proc semFor(c: PContext, n: PNode): PNode =
   # propagate any enforced VoidContext:
   if n.sons[length-1].typ == EnforceVoidContext:
     result.typ = EnforceVoidContext
-  closeScope(c.tab)
+  closeScope(c)
 
 proc semRaise(c: PContext, n: PNode): PNode = 
   result = n
@@ -694,7 +695,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
     if a.sons[1].kind != nkEmpty: 
       # We have a generic type declaration here. In generic types,
       # symbol lookup needs to be done here.
-      openScope(c.tab)
+      openScope(c)
       pushOwner(s)
       if s.magic == mNone: s.typ.kind = tyGenericBody
       # XXX for generic type aliases this is not correct! We need the
@@ -719,7 +720,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         body.size = -1 # could not be computed properly
       s.typ.sons[sonsLen(s.typ) - 1] = body
       popOwner()
-      closeScope(c.tab)
+      closeScope(c)
     elif a.sons[2].kind != nkEmpty: 
       # process the type's body:
       pushOwner(s)
@@ -776,7 +777,7 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) =
 
 proc semBorrow(c: PContext, n: PNode, s: PSym) = 
   # search for the correct alias:
-  var b = SearchForBorrowProc(c, s, c.tab.tos - 2)
+  var b = SearchForBorrowProc(c, c.currentScope.parent, s)
   if b != nil: 
     # store the alias:
     n.sons[bodyPos] = newSymNode(b)
@@ -804,7 +805,7 @@ proc lookupMacro(c: PContext, n: PNode): PSym =
     result = n.sym
     if result.kind notin {skMacro, skTemplate}: result = nil
   else:
-    result = SymtabGet(c.Tab, considerAcc(n), {skMacro, skTemplate})
+    result = searchInScopes(c, considerAcc(n), {skMacro, skTemplate})
 
 proc semProcAnnotation(c: PContext, prc: PNode): PNode =
   var n = prc.sons[pragmasPos]
@@ -839,7 +840,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     s = n[namePos].sym
   pushOwner(s)
-  openScope(c.tab)
+  openScope(c)
   if n.sons[genericParamsPos].kind != nkEmpty:
     illFormedAst(n)           # process parameters:
   if n.sons[paramsPos].kind != nkEmpty: 
@@ -865,7 +866,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
     sideEffectsCheck(c, s)
   else:
     LocalError(n.info, errImplOfXexpected, s.name.s)
-  closeScope(c.tab)           # close scope for parameters
+  closeScope(c)           # close scope for parameters
   popOwner()
   result.typ = s.typ
 
@@ -888,17 +889,48 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
     addResult(c, s.typ.sons[0], n.info, s.kind)
     addResultNode(c, n)
 
-proc semProcAux(c: PContext, n: PNode, kind: TSymKind, 
-                validPragmas: TSpecialWords): PNode = 
+type
+  TProcCompilationSteps = enum
+    stepRegisterSymbol,
+    stepDetermineType,
+    stepCompileBody
+
+proc isForwardDecl(s: PSym): bool =
+  InternalAssert s.kind == skProc
+  result = s.ast[bodyPos].kind != nkEmpty
+
+proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
+                validPragmas: TSpecialWords,
+                phase = stepRegisterSymbol): PNode =
   result = semProcAnnotation(c, n)
   if result != nil: return result
   result = n
   checkSonsLen(n, bodyPos + 1)
-  var s = semIdentDef(c, n.sons[0], kind)
-  n.sons[namePos] = newSymNode(s)
-  s.ast = n
+  var s: PSym
+  var typeIsDetermined = false
+  if n[namePos].kind != nkSym:
+    assert phase == stepRegisterSymbol
+    s = semIdentDef(c, n.sons[0], kind)
+    n.sons[namePos] = newSymNode(s)
+    s.ast = n
+    s.scope = c.currentScope
+
+    if sfNoForward in c.module.flags and
+       sfSystemModule notin c.module.flags:
+      addInterfaceOverloadableSymAt(c, c.currentScope, s)
+      s.flags.incl sfForward
+      return
+  else:
+    s = n[namePos].sym
+    typeIsDetermined = s.typ == nil
+    # if typeIsDetermined: assert phase == stepCompileBody
+    # else: assert phase == stepDetermineType
+  # before compiling the proc body, set as current the scope
+  # where the proc was declared
+  let oldScope = c.currentScope
+  c.currentScope = s.scope
   pushOwner(s)
-  openScope(c.tab)
+  openScope(c)
   var gp: PNode
   if n.sons[genericParamsPos].kind != nkEmpty: 
     n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
@@ -921,17 +953,17 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     n.sons[patternPos] = semPattern(c, n.sons[patternPos])
   if s.kind == skIterator: s.typ.flags.incl(tfIterator)
   
-  var proto = SearchForProc(c, s, c.tab.tos-2) # -2 because we have a scope
-                                               # open for parameters
+  var proto = SearchForProc(c, s.scope, s)
   if proto == nil: 
     s.typ.callConv = lastOptionEntry(c).defaultCC
     # add it here, so that recursive procs are possible:
-    # -2 because we have a scope open for parameters
     if sfGenSym in s.flags: nil
-    elif kind in OverloadableSyms: 
-      addInterfaceOverloadableSymAt(c, s, c.tab.tos - 2)
-    else: 
-      addInterfaceDeclAt(c, s, c.tab.tos - 2)
+    elif kind in OverloadableSyms:
+      if not typeIsDetermined:
+        addInterfaceOverloadableSymAt(c, s.scope, s)
+    else:
+      if not typeIsDetermined:
+        addInterfaceDeclAt(c, s.scope, s)
     if n.sons[pragmasPos].kind != nkEmpty:
       pragma(c, s, n.sons[pragmasPos], validPragmas)
     else:
@@ -942,8 +974,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     if sfForward notin proto.flags: 
       WrongRedefinition(n.info, proto.name.s)
     excl(proto.flags, sfForward)
-    closeScope(c.tab)         # close scope with wrong parameter symbols
-    openScope(c.tab)          # open scope for old (correct) parameter symbols
+    closeScope(c)         # close scope with wrong parameter symbols
+    openScope(c)          # open scope for old (correct) parameter symbols
     if proto.ast.sons[genericParamsPos].kind != nkEmpty: 
       addGenericParamListToScope(c, proto.ast.sons[genericParamsPos])
     addParams(c, proto.typ.n, proto.kind)
@@ -992,11 +1024,17 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
       incl(s.flags, sfForward)
     elif sfBorrow in s.flags: semBorrow(c, n, s)
   sideEffectsCheck(c, s)
-  closeScope(c.tab)           # close scope for parameters
+  closeScope(c)           # close scope for parameters
+  c.currentScope = oldScope
   popOwner()
   if n.sons[patternPos].kind != nkEmpty:
     c.patterns.add(s)
-  
+
+proc determineType(c: PContext, s: PSym) =
+  if s.typ != nil: return
+  #if s.magic != mNone: return
+  discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
+
 proc semIterator(c: PContext, n: PNode): PNode =
   result = semProcAux(c, n, skIterator, iteratorPragmas)
   var s = result.sons[namePos].sym
@@ -1059,7 +1097,7 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
   if n.sons[bodyPos].kind == nkEmpty:
     LocalError(n.info, errImplOfXexpected, s.name.s)
   
-proc evalInclude(c: PContext, n: PNode): PNode = 
+proc evalInclude(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   addSon(result, n)
   for i in countup(0, sonsLen(n) - 1): 
@@ -1183,6 +1221,6 @@ proc SemStmt(c: PContext, n: PNode): PNode =
   result = semExprNoType(c, n)
 
 proc semStmtScope(c: PContext, n: PNode): PNode =
-  openScope(c.tab)
+  openScope(c)
   result = semStmt(c, n)
-  closeScope(c.tab)
+  closeScope(c)
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index d0914d7c9..68abc9aa6 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -130,8 +130,8 @@ proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode
 
-proc openScope(c: var TemplCtx) = openScope(c.c.tab)
-proc closeScope(c: var TemplCtx) = closeScope(c.c.tab)
+proc openScope(c: var TemplCtx) = openScope(c.c)
+proc closeScope(c: var TemplCtx) = closeScope(c.c)
 
 proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode = 
   openScope(c)
@@ -379,7 +379,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     s = semIdentVis(c, skTemplate, n.sons[0], {})
   # check parameter list:
   pushOwner(s)
-  openScope(c.tab)
+  openScope(c)
   n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
   if n.sons[pragmasPos].kind != nkEmpty:
     pragma(c, s, n.sons[pragmasPos], templatePragmas)
@@ -422,18 +422,17 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
     n.sons[bodyPos] = transformToExpr(n.sons[bodyPos]) 
     # only parameters are resolved, no type checking is performed
-  closeScope(c.tab)
+  closeScope(c)
   popOwner()
   s.ast = n
   result = n
   if n.sons[bodyPos].kind == nkEmpty: 
     LocalError(n.info, errImplOfXexpected, s.name.s)
-  let curScope = c.tab.tos - 1
-  var proto = SearchForProc(c, s, curScope)
+  var proto = SearchForProc(c, c.currentScope, s)
   if proto == nil:
-    addInterfaceOverloadableSymAt(c, s, curScope)
+    addInterfaceOverloadableSymAt(c, c.currentScope, s)
   else:
-    SymTabReplace(c.tab.stack[curScope], proto, s)
+    SymTabReplace(c.currentScope.symbols, proto, s)
   if n.sons[patternPos].kind != nkEmpty:
     c.patterns.add(s)
 
@@ -551,7 +550,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
       result.sons[i] = semPatternBody(c, n.sons[i])
 
 proc semPattern(c: PContext, n: PNode): PNode =
-  openScope(c.tab)
+  openScope(c)
   var ctx: TemplCtx
   ctx.toBind = initIntSet()
   ctx.toMixin = initIntSet()
@@ -563,4 +562,4 @@ proc semPattern(c: PContext, n: PNode): PNode =
       result = result.sons[0]
     elif result.len == 0:
       LocalError(n.info, errInvalidExpression)
-  closeScope(c.tab)
+  closeScope(c)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 3abc994ef..cb46dad76 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -79,7 +79,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       incl(e.flags, sfExported)
       if not isPure: StrTableAdd(c.module.tab, e)
     addSon(result.n, newSymNode(e))
-    if sfGenSym notin e.flags and not isPure: addDeclAt(c, e, c.tab.tos - 1)
+    if sfGenSym notin e.flags and not isPure: addDecl(c, e)
     inc(counter)
 
 proc semSet(c: PContext, n: PNode, prev: PType): PType = 
@@ -570,19 +570,17 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
   # if id is not nil, the generic param will bind just once (see below)
   case paramType.kind:
   of tyExpr:
-    if procKind notin {skTemplate, skMacro}:
-      if paramType.sonsLen == 0:
-        # proc(a, b: expr)
-        # no constraints, treat like generic param
-        result.typ = newTypeS(tyGenericParam, c)
-      else:
-        # proc(a: expr{string}, b: expr{nkLambda})
-        # overload on compile time values and AST trees
-        result.typ = newTypeS(tyExpr, c)
-        result.typ.sons = paramType.sons
+    if paramType.sonsLen == 0:
+      # proc(a, b: expr)
+      # no constraints, treat like generic param
+      result.typ = newTypeS(tyGenericParam, c)
+    else:
+      # proc(a: expr{string}, b: expr{nkLambda})
+      # overload on compile time values and AST trees
+      result.typ = newTypeS(tyExpr, c)
+      result.typ.sons = paramType.sons
   of tyTypeDesc:
-    if procKind notin {skTemplate, skMacro} and 
-       tfInstantiated notin paramType.flags:
+     if tfInstantiated notin paramType.flags:
       result.typ = newTypeS(tyTypeDesc, c)
       result.typ.sons = paramType.sons
   of tyDistinct:
@@ -604,11 +602,16 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
 proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
                    paramType: PType, paramName: string,
                    info: TLineInfo): PType =
+  result = paramType
+  if procKind in {skMacro, skTemplate}:
+    # generic param types in macros and templates affect overload
+    # resolution, but don't work as generic params when it comes
+    # to proc instantiation. We don't need to lift such params here.  
+    return
   ## Params having implicit generic types or pseudo types such as 'expr'
   ## need to be added to the generic params lists. 
   ## 'expr' is different from 'expr{string}' so we must first call 
   ## paramTypeClass to get the actual type we are going to use.
-  result = paramType
   var (typeClass, paramTypId) = paramTypeClass(c, paramType, procKind)
   let isAnon = paramTypId == nil
   if typeClass != nil:
@@ -616,7 +619,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     if genericParams == nil:
       # genericParams is nil when the proc is being instantiated
       # the resolved type will be in scope then
-      let s = SymtabGet(c.tab, paramTypId)
+      let s = searchInScopes(c, paramTypId)
       # tests/run/tinterf triggers this:
       if s != nil: result = s.typ
       else:
@@ -734,13 +737,13 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
 proc semBlockType(c: PContext, n: PNode, prev: PType): PType = 
   Inc(c.p.nestedBlockCounter)
   checkSonsLen(n, 2)
-  openScope(c.tab)
+  openScope(c)
   if n.sons[0].kind notin {nkEmpty, nkSym}:
     addDecl(c, newSymS(skLabel, n.sons[0], c))
   result = semStmtListType(c, n.sons[1], prev)
   n.sons[1].typ = result
   n.typ = result
-  closeScope(c.tab)
+  closeScope(c)
   Dec(c.p.nestedBlockCounter)
 
 proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
@@ -749,7 +752,7 @@ proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
   when false:
     if n.kind == nkSym:
       # for generics we need to lookup the type var again:
-      var s = SymtabGet(c.Tab, n.sym.name)
+      var s = searchInScopes(c, n.sym.name)
       if s != nil:
         if s.kind == skType and s.typ != nil:
           var t = n.sym.typ
@@ -910,7 +913,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = newConstraint(c, tyProc)
     else:
       checkSonsLen(n, 2)
-      openScope(c.tab)
+      openScope(c)
       result = semProcTypeNode(c, n.sons[0], nil, prev, skProc)
       # dummy symbol for `pragma`:
       var s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c)
@@ -922,7 +925,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else:
         pragma(c, s, n.sons[1], procTypePragmas)
         when useEffectSystem: SetEffectsForProcType(result, n.sons[1])
-      closeScope(c.tab)
+      closeScope(c)
     if n.kind == nkIteratorTy:
       result.flags.incl(tfIterator)
       result.callConv = ccClosure
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index e1882e1fb..3eb485f8f 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -592,14 +592,20 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     else:
       result = typeRel(c, x, a) # check if it fits
   of tyTypeDesc:
-    if a.kind == tyTypeDesc:
-      if f.sonsLen == 0:
-        result = isGeneric
+    var prev = PType(idTableGet(c.bindings, f))
+    if prev == nil:
+      if a.kind == tyTypeDesc:
+        if f.sonsLen == 0:
+          result = isGeneric
+        else:
+          result = matchTypeClass(c, f, a.sons[0])
+        if result == isGeneric:
+          put(c.bindings, f, a)
       else:
-        result = matchTypeClass(c, f, a.sons[0])
-      if result == isGeneric: put(c.bindings, f, a)
+        result = isNone
     else:
-      result = isNone
+      InternalAssert prev.sonsLen == 1
+      result = typeRel(c, prev.sons[0], a)
   of tyExpr, tyStmt:
     result = isGeneric
   of tyProxy:
@@ -683,11 +689,12 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
 proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 
                         arg, argOrig: PNode): PNode =
   var r: TTypeRelation
-  if f.kind == tyExpr:
-    if f.sonsLen == 0:
+  let fMaybeExpr = f.skipTypes({tyDistinct})
+  if fMaybeExpr.kind == tyExpr:
+    if fMaybeExpr.sonsLen == 0:
       r = isGeneric
     else:
-      let match = matchTypeClass(m, f, a)
+      let match = matchTypeClass(m, fMaybeExpr, a)
       if match != isGeneric: r = isNone
       else:
         # XXX: Ideally, this should happen much earlier somewhere near 
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 18e6dbddf..a5a7fcd66 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -64,11 +64,13 @@ when not defined(nimhygiene):
   {.pragma: inject.}
 
 template wholeSymTab(cond, section: expr) {.immediate.} =
-  for i in countdown(c.tab.tos-1, 0):
-    for item in items(c.tab.stack[i]):
+  var isLocal = true
+  for scope in walkScopes(c.currentScope):
+    if scope == c.topLevelScope: isLocal = false
+    for item in items(scope.symbols):
       let it {.inject.} = item
       if cond:
-        SuggestWriteln(SymToStr(it, isLocal = i > ModuleTablePos, section))
+        SuggestWriteln(SymToStr(it, isLocal = isLocal, section))
         inc outputs
 
 proc suggestSymList(c: PContext, list: PNode, outputs: var int) = 
@@ -123,11 +125,14 @@ proc suggestOperations(c: PContext, n: PNode, typ: PType, outputs: var int) =
 
 proc suggestEverything(c: PContext, n: PNode, outputs: var int) =
   # do not produce too many symbols:
-  for i in countdown(c.tab.tos-1, 1):
-    for it in items(c.tab.stack[i]):
+  var isLocal = true
+  for scope in walkScopes(c.currentScope):
+    if scope == c.topLevelScope: isLocal = false
+    for it in items(scope.symbols):
       if filterSym(it):
-        SuggestWriteln(SymToStr(it, isLocal = i > ModuleTablePos, sectionSuggest))
+        SuggestWriteln(SymToStr(it, isLocal = isLocal, sectionSuggest))
         inc outputs
+    if scope == c.topLevelScope: break
 
 proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
   # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
@@ -138,7 +143,7 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) =
     if n.kind == nkSym and n.sym.kind == skModule: 
       if n.sym == c.module: 
         # all symbols accessible, because we are in the current module:
-        for it in items(c.tab.stack[ModuleTablePos]): 
+        for it in items(c.topLevelScope.symbols):
           if filterSym(it): 
             SuggestWriteln(SymToStr(it, isLocal=false, sectionSuggest))
             inc outputs
diff --git a/compiler/types.nim b/compiler/types.nim
index 6f47a7f2d..3096b73c8 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1016,8 +1016,9 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
   of tyExpr, tyStmt, tyTypeDesc:
     result = true
     # XXX er ... no? these should not be allowed!
-  of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation, 
-      tyTypeClass:
+  of tyTypeClass:
+    result = true
+  of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation:
     result = false
   of tyEmpty, tyNil:
     result = kind == skConst
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 3ad2f45ca..36d08c718 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -53,7 +53,7 @@ type
     wFloatchecks, wNanChecks, wInfChecks,
     wAssertions, wPatterns, wWarnings,
     wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
-    wDeadCodeElim, wSafecode, 
+    wDeadCodeElim, wSafecode, wNoForward,
     wPragma,
     wCompileTime, wNoInit,
     wPassc, wPassl, wBorrow, wDiscardable,
@@ -135,7 +135,7 @@ const
 
     "assertions", "patterns", "warnings", "hints", 
     "optimization", "raises", "writes", "reads", "size", "effects", "tags",
-    "deadcodeelim", "safecode", 
+    "deadcodeelim", "safecode", "noforward",
     "pragma",
     "compiletime", "noinit",
     "passc", "passl", "borrow", "discardable", "fieldchecks",