summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-06-09 23:29:43 +0200
committerAraq <rumpf_a@web.de>2013-06-09 23:29:43 +0200
commit23ef565a3c2c1f83816fefbeadb0fc59e754997d (patch)
tree7a2adeeb6f3215c35ff0b7fdeeee6f9907308931
parent2aaa8f7909e51eb3d971e197f152e247c64362e9 (diff)
downloadNim-23ef565a3c2c1f83816fefbeadb0fc59e754997d.tar.gz
implemented large parts of the 'not nil' checking
-rw-r--r--compiler/ast.nim29
-rw-r--r--compiler/magicsys.nim16
-rw-r--r--compiler/msgs.nim9
-rw-r--r--compiler/parampatterns.nim2
-rw-r--r--compiler/pragmas.nim10
-rw-r--r--compiler/semexprs.nim25
-rw-r--r--compiler/sempass2.nim68
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/semtypes.nim67
-rw-r--r--compiler/semtypinst.nim2
-rw-r--r--compiler/transf.nim7
-rw-r--r--compiler/trees.nim3
-rw-r--r--compiler/wordrecg.nim4
-rw-r--r--doc/manual.txt76
-rw-r--r--doc/nimrodc.txt2
-rw-r--r--lib/pure/times.nim36
-rw-r--r--tests/reject/tuninit1.nim36
-rw-r--r--todo.txt3
-rw-r--r--tools/niminst/debcreation.nim4
-rw-r--r--web/index.txt2
-rw-r--r--web/news.txt2
21 files changed, 293 insertions, 112 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 6e5a17d99..f4b1b84f5 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -353,7 +353,7 @@ type
     nfSem       # node has been checked for semantics
 
   TNodeFlags* = set[TNodeFlag]
-  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 19)
+  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 23)
     tfVarargs,        # procedure has C styled varargs
     tfNoSideEffect,   # procedure type does not allow side effects
     tfFinal,          # is the object final?
@@ -380,7 +380,13 @@ type
     tfByRef,          # pass object/tuple by reference (C backend)
     tfIterator,       # type is really an iterator, not a tyProc
     tfShared,         # type is 'shared'
-    tfNotNil          # type cannot be 'nil'
+    tfNotNil,         # type cannot be 'nil'
+    
+    tfNeedsInit,      # type constains a "not nil" constraint somewhere or some
+                      # other type so that it requires inititalization
+    tfHasShared,      # type constains a "shared" constraint modifier somewhere
+    tfHasMeta,        # type has "typedesc" or "expr" somewhere
+    tfHasGCedMem,     # type contains GC'ed memory
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -1168,14 +1174,25 @@ proc newSons(father: PNode, length: int) =
   else:
     setlen(father.sons, length)
 
-proc addSon*(father, son: PType) {.deprecated.} =
-  if isNil(father.sons): father.sons = @[]
-  add(father.sons, son)
-  #assert((father.kind != tyGenericInvokation) or (son.kind != tyGenericInst))
+proc propagateToOwner*(owner, elem: PType) =
+  owner.flags = owner.flags + (elem.flags * {tfNeedsInit, tfHasShared, 
+                                             tfHasMeta, tfHasGCedMem})
+  if tfNotNil in elem.flags:
+    owner.flags.incl tfNeedsInit
+    
+  if tfShared in elem.flags:
+    owner.flags.incl tfHasShared
+  
+  if elem.kind in {tyExpr, tyTypeDesc}:
+    owner.flags.incl tfHasMeta
+  elif elem.kind in {tyString, tyRef, tySequence} or
+      elem.kind == tyProc and elem.callConv == ccClosure:
+    owner.flags.incl tfHasGCedMem
 
 proc rawAddSon*(father, son: PType) =
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
+  if not son.isNil: propagateToOwner(father, son)
 
 proc addSon(father, son: PNode) = 
   assert son != nil
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index 352b6ca04..1972dec98 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -43,6 +43,18 @@ proc getSysSym(name: string): PSym =
     result.typ = newType(tyError, systemModule)
   if result.kind == skStub: loadStub(result)
   
+proc getSysMagic*(name: string, m: TMagic): PSym =
+  var ti: TIdentIter
+  let id = getIdent(name)
+  result = InitIdentIter(ti, systemModule.tab, id)
+  while result != nil:
+    if result.kind == skStub: loadStub(result)
+    if result.magic == m: return result
+    result = NextIdentIter(ti, systemModule.tab)
+  rawMessage(errSystemNeeds, name)
+  result = newSym(skError, id, systemModule, systemModule.info)
+  result.typ = newType(tyError, systemModule)
+  
 proc sysTypeFromName*(name: string): PType = 
   result = getSysSym(name).typ
 
@@ -111,7 +123,9 @@ proc skipIntLit*(t: PType): PType {.inline.} =
 
 proc AddSonSkipIntLit*(father, son: PType) =
   if isNil(father.sons): father.sons = @[]
-  add(father.sons, son.skipIntLit)
+  let s = son.skipIntLit
+  add(father.sons, s)
+  propagateToOwner(father, s)
 
 proc setIntLitType*(result: PNode) =
   let i = result.intVal
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index f75aec0d5..302a7cfd8 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -106,7 +106,7 @@ type
     warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, 
     warnNilStatement, warnAnalysisLoophole,
     warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
-    warnEachIdentIsTuple, warnShadowIdent, warnUninit, warnUser,
+    warnEachIdentIsTuple, warnShadowIdent, warnProveInit, warnUninit, warnUser,
     hintSuccess, hintSuccessX, 
     hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, 
     hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, 
@@ -355,7 +355,8 @@ const
     warnImplicitClosure: "implicit closure convention: '$1' [ImplicitClosure]",
     warnEachIdentIsTuple: "each identifier is a tuple [EachIdentIsTuple]",
     warnShadowIdent: "shadowed identifier: '$1' [ShadowIdent]",
-    warnUninit: "read from potentially uninitialized variable: '$1' [Uninit]",
+    warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future. [ProveInit]",
+    warnUninit: "'$1' might not have been initialized [Uninit]",
     warnUser: "$1 [User]", 
     hintSuccess: "operation successful [Success]", 
     hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#) [SuccessX]", 
@@ -375,14 +376,14 @@ const
     hintUser: "$1 [User]"]
 
 const
-  WarningsToStr*: array[0..20, string] = ["CannotOpenFile", "OctalEscape", 
+  WarningsToStr*: array[0..21, string] = ["CannotOpenFile", "OctalEscape", 
     "XIsNeverRead", "XmightNotBeenInit",
     "Deprecated", "ConfigDeprecated",
     "SmallLshouldNotBeUsed", "UnknownMagic", 
     "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", 
     "CommentXIgnored", "NilStmt",
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
-    "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "Uninit",
+    "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", "ProveInit", "Uninit",
     "User"]
 
   HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", 
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index de7bcaeee..283f83906 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -50,6 +50,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
 proc whichAlias*(p: PSym): TAliasRequest =
   if p.constraint != nil:
     result = TAliasRequest(p.constraint.strVal[0].ord)
+  else:
+    result = aqNone
 
 proc compileConstraints(p: PNode, result: var TPatternCode) =
   case p.kind
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index cc432aea8..cecec8e5e 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -50,7 +50,7 @@ const
   typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, 
     wPure, wHeader, wCompilerProc, wFinal, wSize, wExtern, wShallow, 
     wImportcpp, wImportobjc, wError, wIncompleteStruct, wByCopy, wByRef,
-    wInheritable, wGenSym, wInject}
+    wInheritable, wGenSym, wInject, wRequiresInit}
   fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, 
     wImportcpp, wImportobjc, wError}
   varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, 
@@ -253,11 +253,11 @@ proc processNote(c: PContext, n: PNode) =
     of wHint:
       var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
       if x >= 0: nk = TNoteKind(x + ord(hintMin))
-      else: invalidPragma(n)
+      else: invalidPragma(n); return
     of wWarning:
       var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
       if x >= 0: nk = TNoteKind(x + ord(warnMin))
-      else: InvalidPragma(n)
+      else: InvalidPragma(n); return
     else:
       invalidPragma(n)
       return
@@ -695,6 +695,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
           noVal(it)
           if sym.typ == nil: invalidPragma(it)
           else: incl(sym.typ.flags, tfIncompleteStruct)
+        of wRequiresInit:
+          noVal(it)
+          if sym.typ == nil: invalidPragma(it)
+          else: incl(sym.typ.flags, tfNeedsInit)
         of wByRef:
           noVal(it)
           if sym == nil or sym.typ == nil:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fba028a46..cdab9b535 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1601,6 +1601,25 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
     addSonSkipIntLit(typ, n.sons[i].typ)
   result.typ = typ
 
+proc checkInitialized(n: PNode, ids: TIntSet, info: TLineInfo) =
+  case n.kind
+  of nkRecList:
+    for i in countup(0, sonsLen(n) - 1):
+      checkInitialized(n.sons[i], ids, info)
+  of nkRecCase:
+    if (n.sons[0].kind != nkSym): InternalError(info, "checkInitialized")
+    checkInitialized(n.sons[0], ids, info)
+    when false:
+      # XXX we cannot check here, as we don't know the branch!
+      for i in countup(1, sonsLen(n) - 1):
+        case n.sons[i].kind
+        of nkOfBranch, nkElse: checkInitialized(lastSon(n.sons[i]), ids, info)
+        else: internalError(info, "checkInitialized")
+  of nkSym:
+    if tfNeedsInit in n.sym.typ.flags and n.sym.name.id notin ids:
+      Message(info, errGenerated, "field not initialized: " & n.sym.name.s)
+  else: internalError(info, "checkInitialized")
+
 proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   var t = semTypeNode(c, n.sons[0], nil)
   result = n
@@ -1611,6 +1630,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if t.kind != tyObject:
     localError(n.info, errGenerated, "object constructor needs an object type")
     return
+  var objType = t
   var ids = initIntSet()
   for i in 1.. <n.len:
     let it = n.sons[i]
@@ -1642,6 +1662,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
       localError(it.info, errUndeclaredFieldX, id.s)
     it.sons[1] = e
     # XXX object field name check for 'case objects' if the kind is static?
+  if tfNeedsInit in objType.flags:
+    while true:
+      checkInitialized(objType.n, ids, n.info)
+      if objType.sons[0] == nil: break
+      objType = skipTypes(objType.sons[0], {tyGenericInst})
 
 proc semBlock(c: PContext, n: PNode): PNode =
   result = n
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 64aae09c1..151cb9cbc 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -9,7 +9,7 @@
 
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, 
-  wordrecg, strutils, options
+  wordrecg, strutils, options, guards
 
 # Second semantic checking pass over the AST. Necessary because the old
 # way had some inherent problems. Performs:
@@ -75,7 +75,7 @@ type
     owner: PSym
     init: seq[int] # list of initialized variables
                    # coming soon: "guard" tracking for 'let' variables
-  
+    guards: TModel # nested guards
   PEffects = var TEffects
 
 proc isLocalVar(a: PEffects, s: PSym): bool =
@@ -93,11 +93,14 @@ proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if isLocalVar(a, s):
     if s.id notin a.init:
-      if true:
-        Message(n.info, warnUninit, s.name.s)
+      if tfNeedsInit in s.typ.flags:
+        when true:
+          Message(n.info, warnProveInit, s.name.s)
+        else:
+          Message(n.info, errGenerated,
+            "'$1' might not have been initialized" % s.name.s)
       else:
-        Message(n.info, errGenerated,
-          "read from potentially uninitialized variable: '$1'" % s.name.s)
+        Message(n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
 
@@ -295,7 +298,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let tagSpec = effectSpec(pragma, wTags)
   mergeTags(tracked, tagSpec, n)
 
-proc trackOperand(tracked: PEffects, n: PNode) =
+proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
   let op = n.typ
   if op != nil and op.kind == tyProc and n.kind != nkNilLit:
     InternalAssert op.n.sons[0].kind == nkEffectList
@@ -312,16 +315,40 @@ proc trackOperand(tracked: PEffects, n: PNode) =
     else:
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
+  if paramType != nil and tfNotNil in paramType.flags and
+      op != nil and tfNotNil notin op.flags:
+    case impliesNotNil(tracked.guards, n)
+    of impUnknown:
+      Message(n.info, errGenerated, 
+              "cannot prove '$1' is not nil" % n.renderTree)
+    of impNo:
+      Message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree)
+    of impYes: discard
+
+proc breaksBlock(n: PNode): bool =
+  case n.kind
+  of nkStmtList, nkStmtListExpr:
+    for c in n: 
+      if breaksBlock(c): return true
+  of nkBreakStmt, nkReturnStmt, nkRaiseStmt:
+    return true
+  of nkCallKinds:
+    if n.sons[0].kind == nkSym and sfNoReturn in n.sons[0].sym.flags:
+      return true
+  else:
+    discard
 
 proc trackCase(tracked: PEffects, n: PNode) =
   track(tracked, n.sons[0])
   let oldState = tracked.init.len
   var inter: TIntersection = @[]
+  var toCover = 0
   for i in 1.. <n.len:
     let branch = n.sons[i]
     setLen(tracked.init, oldState)
     for i in 0 .. <branch.len:
       track(tracked, branch.sons[i])
+    if not breaksBlock(branch.lastSon): inc toCover
     for i in oldState.. <tracked.init.len:
       addToIntersection(inter, tracked.init[i])
   let exh = case skipTypes(n.sons[0].Typ, abstractVarRange-{tyTypeDesc}).Kind
@@ -332,30 +359,41 @@ proc trackCase(tracked: PEffects, n: PNode) =
   setLen(tracked.init, oldState)
   if exh:
     for id, count in items(inter):
-      if count == n.len-1: tracked.init.add id
+      if count >= toCover: tracked.init.add id
     # else we can't merge
 
 proc trackIf(tracked: PEffects, n: PNode) =
   track(tracked, n.sons[0].sons[0])
+  let oldFacts = tracked.guards.len
+  addFact(tracked.guards, n.sons[0].sons[0])
   let oldState = tracked.init.len
 
   var inter: TIntersection = @[]
+  var toCover = 0
   track(tracked, n.sons[0].sons[1])
+  if not breaksBlock(n.sons[0].sons[1]): inc toCover
   for i in oldState.. <tracked.init.len:
     addToIntersection(inter, tracked.init[i])
 
   for i in 1.. <n.len:
     let branch = n.sons[i]
+    setLen(tracked.guards, oldFacts)
+    for j in 0..i-1:
+      addFactNeg(tracked.guards, n.sons[j].sons[0])
+    if branch.len > 1:
+      addFact(tracked.guards, branch.sons[0])
     setLen(tracked.init, oldState)
     for i in 0 .. <branch.len:
       track(tracked, branch.sons[i])
+    if not breaksBlock(branch.lastSon): inc toCover
     for i in oldState.. <tracked.init.len:
       addToIntersection(inter, tracked.init[i])
   setLen(tracked.init, oldState)
   if lastSon(n).len == 1:
     for id, count in items(inter):
-      if count == n.len: tracked.init.add id
+      if count >= toCover: tracked.init.add id
     # else we can't merge as it is not exhaustive
+  setLen(tracked.guards, oldFacts)
   
 proc trackBlock(tracked: PEffects, n: PNode) =
   if n.kind in {nkStmtList, nkStmtListExpr}:
@@ -377,6 +415,9 @@ proc isTrue(n: PNode): bool =
   n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
     n.kind == nkIntLit and n.intVal != 0
 
+proc paramType(op: PType, i: int): PType =
+  if op != nil and i < op.len: result = op.sons[i]
+
 proc track(tracked: PEffects, n: PNode) =
   case n.kind
   of nkSym:
@@ -404,11 +445,12 @@ proc track(tracked: PEffects, n: PNode) =
       else:
         mergeEffects(tracked, effectList.sons[exceptionEffects], n)
         mergeTags(tracked, effectList.sons[tagEffects], n)
-    for i in 1 .. <len(n): trackOperand(tracked, n.sons[i])
+    for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
     if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, 
                                            mNewSeq, mShallowCopy}:
       # may not look like an assignment, but it is:
       initVar(tracked, n.sons[1])
+      # XXX new(objWithNotNil) is not initialized properly!
     for i in 0 .. <safeLen(n):
       track(tracked, n.sons[i])
   of nkTryStmt: trackTryStmt(tracked, n)
@@ -510,8 +552,14 @@ proc trackProc*(s: PSym, body: PNode) =
   t.tags = effects.sons[tagEffects]
   t.owner = s
   t.init = @[]
+  t.guards = @[]
   track(t, body)
   
+  if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
+      s.kind in {skProc, skConverter, skMethod}:
+    var res = s.ast.sons[resultPos].sym # get result symbol
+    if res.id notin t.init:
+      Message(body.info, warnProveInit, "result")
   let p = s.ast.sons[pragmasPos]
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 6123957cd..75ab9920d 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -374,7 +374,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
             if warnShadowIdent in gNotes and not identWithin(def, v.name):
               Message(a.info, warnShadowIdent, v.name.s)
       if def != nil and def.kind != nkEmpty:
-        # this is only needed for the evaluation pass:
+        # this is needed for the evaluation pass and for the guard checking:
         v.ast = def
         if sfThread in v.flags: LocalError(def.info, errThreadvarCannotInit)
       if a.kind != nkVarTuple:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 658b3507f..769e2ab7d 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -23,7 +23,7 @@ proc newConstraint(c: PContext, k: TTypeKind): PType =
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   if n.sonsLen == 0: return newConstraint(c, tyEnum)
-  var 
+  var
     counter, x: BiggestInt
     e: PSym
     base: PType
@@ -39,6 +39,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     counter = lastOrd(base) + 1
   rawAddSon(result, base)
   let isPure = result.sym != nil and sfPure in result.sym.flags
+  var hasNull = false
   for i in countup(1, sonsLen(n) - 1): 
     case n.sons[i].kind
     of nkEnumFieldDef: 
@@ -74,6 +75,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     else: illFormedAst(n)
     e.typ = result
     e.position = int(counter)
+    if e.position == 0: hasNull = true
     if result.sym != nil and sfExported in result.sym.flags:
       incl(e.flags, sfUsed)
       incl(e.flags, sfExported)
@@ -81,6 +83,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     addSon(result.n, newSymNode(e))
     if sfGenSym notin e.flags and not isPure: addDecl(c, e)
     inc(counter)
+  if not hasNull: incl(result.flags, tfNeedsInit)
 
 proc semSet(c: PContext, n: PNode, prev: PType): PType = 
   result = newOrPrevType(tySet, prev, c)
@@ -168,7 +171,14 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
 proc semRange(c: PContext, n: PNode, prev: PType): PType =
   result = nil
   if sonsLen(n) == 2:
-    if isRange(n[1]): result = semRangeAux(c, n[1], prev)
+    if isRange(n[1]):
+      result = semRangeAux(c, n[1], prev)
+      let n = result.n
+      if n.sons[0].kind in {nkCharLit..nkUInt64Lit}:
+        if n.sons[0].intVal > 0 or n.sons[1].intVal < 0:
+          incl(result.flags, tfNeedsInit)
+      elif n.sons[0].floatVal > 0.0 or n.sons[1].floatVal < 0.0:
+        incl(result.flags, tfNeedsInit)
     else:
       LocalError(n.sons[0].info, errRangeExpected)
       result = newOrPrevType(tyError, prev, c)
@@ -386,34 +396,34 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
           swap(branch.sons[L-2], branch.sons[L-1])
     checkForOverlap(c, t, i, branchIndex)
     
-proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, 
-                      father: PNode, rectype: PSym)
-proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int, 
-                   father: PNode, rectype: PSym) = 
+proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
+                      father: PNode, rectype: PType)
+proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
+                   father: PNode, rectype: PType) =
   var a = copyNode(n)
   checkMinSonsLen(n, 2)
   semRecordNodeAux(c, n.sons[0], check, pos, a, rectype)
-  if a.sons[0].kind != nkSym: 
+  if a.sons[0].kind != nkSym:
     internalError("semRecordCase: discriminant is no symbol")
     return
   incl(a.sons[0].sym.flags, sfDiscriminant)
   var covered: biggestInt = 0
   var typ = skipTypes(a.sons[0].Typ, abstractVar-{tyTypeDesc})
-  if not isOrdinalType(typ): 
+  if not isOrdinalType(typ):
     LocalError(n.info, errSelectorMustBeOrdinal)
-  elif firstOrd(typ) < 0: 
+  elif firstOrd(typ) < 0:
     LocalError(n.info, errOrdXMustNotBeNegative, a.sons[0].sym.name.s)
-  elif lengthOrd(typ) > 0x00007FFF: 
+  elif lengthOrd(typ) > 0x00007FFF:
     LocalError(n.info, errLenXinvalid, a.sons[0].sym.name.s)
   var chckCovered = true
-  for i in countup(1, sonsLen(n) - 1): 
+  for i in countup(1, sonsLen(n) - 1):
     var b = copyTree(n.sons[i])
     addSon(a, b)
     case n.sons[i].kind
-    of nkOfBranch: 
+    of nkOfBranch:
       checkMinSonsLen(b, 2)
       semCaseBranch(c, a, b, i, covered)
-    of nkElse: 
+    of nkElse:
       chckCovered = false
       checkSonsLen(b, 1)
     else: illFormedAst(n)
@@ -424,7 +434,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var TIntSet, pos: var int,
   addSon(father, a)
 
 proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int, 
-                      father: PNode, rectype: PSym) = 
+                      father: PNode, rectype: PType) =
   if n == nil: return
   case n.kind
   of nkRecWhen:
@@ -463,7 +473,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
     semRecordCase(c, n, check, pos, father, rectype)
   of nkNilLit: 
     if father.kind != nkRecList: addSon(father, newNodeI(nkRecList, n.info))
-  of nkRecList: 
+  of nkRecList:
     # attempt to keep the nesting at a sane level:
     var a = if father.kind == nkRecList: father else: copyNode(n)
     for i in countup(0, sonsLen(n) - 1): 
@@ -473,7 +483,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
     checkMinSonsLen(n, 3)
     var length = sonsLen(n)
     var a: PNode
-    if father.kind != nkRecList and length >= 4: a = newNodeI(nkRecList, n.info)
+    if father.kind != nkRecList and length>=4: a = newNodeI(nkRecList, n.info)
     else: a = ast.emptyNode
     if n.sons[length-1].kind != nkEmpty: 
       localError(n.sons[length-1].info, errInitHereNotAllowed)
@@ -483,17 +493,19 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
       typ = errorType(c)
     else:
       typ = semTypeNode(c, n.sons[length-2], nil)
+      propagateToOwner(rectype, typ)
+    let rec = rectype.sym
     for i in countup(0, sonsLen(n)-3):
       var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
       suggestSym(n.sons[i], f)
       f.typ = typ
       f.position = pos
-      if (rectype != nil) and ({sfImportc, sfExportc} * rectype.flags != {}) and
+      if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and
           (f.loc.r == nil): 
         f.loc.r = toRope(f.name.s)
-        f.flags = f.flags + ({sfImportc, sfExportc} * rectype.flags)
+        f.flags = f.flags + ({sfImportc, sfExportc} * rec.flags)
       inc(pos)
-      if ContainsOrIncl(check, f.name.id): 
+      if ContainsOrIncl(check, f.name.id):
         localError(n.sons[i].info, errAttemptToRedefine, f.name.s)
       if a.kind == nkEmpty: addSon(father, newSymNode(f))
       else: addSon(a, newSymNode(f))
@@ -502,20 +514,20 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var TIntSet, pos: var int,
   else: illFormedAst(n)
   
 proc addInheritedFieldsAux(c: PContext, check: var TIntSet, pos: var int, 
-                           n: PNode) = 
+                           n: PNode) =
   case n.kind
-  of nkRecCase: 
+  of nkRecCase:
     if (n.sons[0].kind != nkSym): InternalError(n.info, "addInheritedFieldsAux")
     addInheritedFieldsAux(c, check, pos, n.sons[0])
-    for i in countup(1, sonsLen(n) - 1): 
+    for i in countup(1, sonsLen(n) - 1):
       case n.sons[i].kind
-      of nkOfBranch, nkElse: 
+      of nkOfBranch, nkElse:
         addInheritedFieldsAux(c, check, pos, lastSon(n.sons[i]))
       else: internalError(n.info, "addInheritedFieldsAux(record case branch)")
-  of nkRecList: 
-    for i in countup(0, sonsLen(n) - 1): 
+  of nkRecList:
+    for i in countup(0, sonsLen(n) - 1):
       addInheritedFieldsAux(c, check, pos, n.sons[i])
-  of nkSym: 
+  of nkSym:
     Incl(check, n.sym.name.id)
     inc(pos)
   else: InternalError(n.info, "addInheritedFieldsAux()")
@@ -553,7 +565,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
   result = newOrPrevType(tyObject, prev, c)
   rawAddSon(result, base)
   result.n = newNodeI(nkRecList, n.info)
-  semRecordNodeAux(c, n.sons[2], check, pos, result.n, result.sym)
+  semRecordNodeAux(c, n.sons[2], check, pos, result.n, result)
   if n.sons[0].kind != nkEmpty:
     # dummy symbol for `pragma`:
     var s = newSymS(skType, newIdentNode(getIdent("dummy"), n.info), c)
@@ -853,6 +865,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         if result.kind in NilableTypes and n.sons[2].kind == nkNilLit:
           result = freshType(result, prev)
           result.flags.incl(tfNotNil)
+          result.flags.incl(tfNeedsInit)
         else:
           LocalError(n.info, errGenerated, "invalid type")
       else:
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 26341525c..31fbc33e1 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -152,6 +152,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
       x = lookupTypeVar(cl, x)
       if header == nil: header = copyType(t, t.owner, false)
       header.sons[i] = x
+      propagateToOwner(header, x)
       #idTablePut(cl.typeMap, body.sons[i-1], x)
   if header != nil:
     # search again after first pass:
@@ -170,6 +171,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
     var x = replaceTypeVarsT(cl, t.sons[i])
     assert x.kind != tyGenericInvokation
     header.sons[i] = x
+    propagateToOwner(header, x)
     idTablePut(cl.typeMap, body.sons[i-1], x)
   
   for i in countup(1, sonsLen(t) - 1): 
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 058143cdd..a35669e1d 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -504,8 +504,8 @@ proc transformCase(c: PTransf, n: PNode): PTransNode =
     result.add(elseBranch)
   elif result.Pnode.lastSon.kind != nkElse and not (
       skipTypes(n.sons[0].Typ, abstractVarRange).Kind in
-        {tyInt..tyInt64, tyChar, tyEnum}):
-    # fix a stupid code gen bug by normalizing: 
+        {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}):
+    # fix a stupid code gen bug by normalizing:
     var elseBranch = newTransNode(nkElse, n.info, 1)
     elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
     add(result, elseBranch)
@@ -704,7 +704,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
   if nfTransf in n.flags or prc.kind in {skTemplate, skMacro}:
     result = n
   else:
-    when useEffectSystem: trackProc(prc, n)
+    #when useEffectSystem: trackProc(prc, n)
     var c = openTransf(module, "")
     result = processTransf(c, n)
     if prc.kind != skMacro:
@@ -713,6 +713,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
     if prc.kind == skIterator and prc.typ.callConv == ccClosure:
       result = lambdalifting.liftIterator(prc, result)
     incl(result.flags, nfTransf)
+    when useEffectSystem: trackProc(prc, result)
 
 proc transformStmt*(module: PSym, n: PNode): PNode =
   if nfTransf in n.flags:
diff --git a/compiler/trees.nim b/compiler/trees.nim
index f371cb021..ab5c97a19 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -93,8 +93,7 @@ proc getOpSym*(op: PNode): PSym =
   
 proc getMagic*(op: PNode): TMagic = 
   case op.kind
-  of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix,
-     nkInfix: 
+  of nkCallKinds:
     case op.sons[0].Kind
     of nkSym: result = op.sons[0].sym.magic
     else: result = mNone
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 36d08c718..2a8a02bd6 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -41,7 +41,7 @@ type
     
     wImmediate, wDestructor, wImportCpp, wImportObjC,
     wImportCompilerProc,
-    wImportc, wExportc, wIncompleteStruct,
+    wImportc, wExportc, wIncompleteStruct, wRequiresInit,
     wAlign, wNodecl, wPure, wSideeffect, wHeader,
     wNosideeffect, wNoreturn, wMerge, wLib, wDynlib, wCompilerproc, wProcVar, 
     wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef, 
@@ -122,7 +122,7 @@ const
     
     "immediate", "destructor", "importcpp", "importobjc",
     "importcompilerproc", "importc", "exportc", "incompletestruct",
-    "align", "nodecl", "pure", "sideeffect",
+    "requiresinit", "align", "nodecl", "pure", "sideeffect",
     "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", 
     "compilerproc", "procvar", "fatal", "error", "warning", "hint", "line", 
     "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", 
diff --git a/doc/manual.txt b/doc/manual.txt
index f163e0d5f..7147eb631 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -327,36 +327,36 @@ Numerical constants
 
 `Numerical constants`:idx: are of a single type and have the form::
 
-  hexdigit ::= digit | 'A'..'F' | 'a'..'f'
-  octdigit ::= '0'..'7'
-  bindigit ::= '0'..'1'
-  HEX_LIT ::= '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
-  DEC_LIT ::= digit ( ['_'] digit )*
-  OCT_LIT ::= '0o' octdigit ( ['_'] octdigit )*
-  BIN_LIT ::= '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
+  hexdigit = digit | 'A'..'F' | 'a'..'f'
+  octdigit = '0'..'7'
+  bindigit = '0'..'1'
+  HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
+  DEC_LIT = digit ( ['_'] digit )*
+  OCT_LIT = '0o' octdigit ( ['_'] octdigit )*
+  BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
   
-  INT_LIT ::= HEX_LIT
-            | DEC_LIT
-            | OCT_LIT
-            | BIN_LIT
-
-  INT8_LIT ::= INT_LIT ['\''] ('i' | 'I') '8'
-  INT16_LIT ::= INT_LIT ['\''] ('i' | 'I') '16'
-  INT32_LIT ::= INT_LIT ['\''] ('i' | 'I') '32'
-  INT64_LIT ::= INT_LIT ['\''] ('i' | 'I') '64'
-
-  UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U')
-  UINT8_LIT ::= INT_LIT ['\''] ('u' | 'U') '8'
-  UINT16_LIT ::= INT_LIT ['\''] ('u' | 'U') '16'
-  UINT32_LIT ::= INT_LIT ['\''] ('u' | 'U') '32'
-  UINT64_LIT ::= INT_LIT ['\''] ('u' | 'U') '64'
-
-  exponent ::= ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
-  FLOAT_LIT ::= digit (['_'] digit)*  ('.' (['_'] digit)* [exponent] |exponent)
-  FLOAT32_LIT ::= HEX_LIT '\'' ('f'|'F') '32'
-             | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
-  FLOAT64_LIT ::= HEX_LIT '\'' ('f'|'F') '64'
-             | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
+  INT_LIT = HEX_LIT
+          | DEC_LIT
+          | OCT_LIT
+          | BIN_LIT
+
+  INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
+  INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
+  INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
+  INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
+
+  UINT8_LIT = INT_LIT ['\''] ('u' | 'U')
+  UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
+  UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
+  UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
+  UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
+
+  exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
+  FLOAT_LIT = digit (['_'] digit)*  ('.' (['_'] digit)* [exponent] |exponent)
+  FLOAT32_LIT = HEX_LIT '\'' ('f'|'F') '32'
+              | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '32'
+  FLOAT64_LIT = HEX_LIT '\'' ('f'|'F') '64'
+              | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] ('f'|'F') '64'
 
 
 As can be seen in the productions, numerical constants can contain underscores
@@ -1818,6 +1818,24 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit
   proc returnUndefinedValue: int {.noinit.} = nil
 
 
+The implicit initialization can be also prevented by the `requiresInit`:idx:
+type pragma. The compiler requires an explicit initialization then. However
+it does a `control flow analysis`:idx: to prove the variable has been 
+initialized and does not rely on syntactic properties:
+
+.. code-block:: nimrod
+  type
+    TMyObject = object {.requiresInit.}
+    
+  proc p() =
+    # the following is valid:
+    var x: TMyObject
+    if someCondition():
+      x = a()
+    else:
+      x = a()
+    use x
+
 let statement
 -------------
 
diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt
index 7f77a50d2..339cee382 100644
--- a/doc/nimrodc.txt
+++ b/doc/nimrodc.txt
@@ -20,7 +20,7 @@ on the different supported platforms. It is not a definition of the Nimrod
 programming language (therefore is the `manual <manual.html>`_).

 

 Nimrod is free software; it is licensed under the

-`GNU General Public License <gpl.html>`_.

+`MIT License <http://www.opensource.org/licenses/mit-license.php>`_.

 

 

 Compiler Usage

diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 2eb8d692b..86609c8e3 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -338,24 +338,24 @@ when not defined(JS):
     const
       weekDays: array [0..6, TWeekDay] = [
         dSun, dMon, dTue, dWed, dThu, dFri, dSat]
-    result.second = int(tm.second)
-    result.minute = int(tm.minute)
-    result.hour = int(tm.hour)
-    result.monthday = int(tm.monthday)
-    result.month = TMonth(tm.month)
-    result.year = tm.year + 1900'i32
-    result.weekday = weekDays[int(tm.weekDay)]
-    result.yearday = int(tm.yearday)
-    result.isDST = tm.isDST > 0
-    if local:
-      if result.isDST:
-        result.tzname = getTzname().DST
-      else:
-        result.tzname = getTzname().nonDST
-    else:
-      result.tzname = "UTC"
-    
-    result.timezone = if local: getTimezone() else: 0
+    TTimeInfo(second: int(tm.second),
+      minute: int(tm.minute),
+      hour: int(tm.hour),
+      monthday: int(tm.monthday),
+      month: TMonth(tm.month),
+      year: tm.year + 1900'i32,
+      weekday: weekDays[int(tm.weekDay)],
+      yearday: int(tm.yearday),
+      isDST: tm.isDST > 0,
+      tzname: if local:
+          if tm.isDST > 0:
+            getTzname().DST
+          else:
+            getTzname().nonDST
+        else:
+          "UTC",
+      timezone: if local: getTimezone() else: 0
+    )
   
   proc timeInfoToTM(t: TTimeInfo): structTM =
     const
diff --git a/tests/reject/tuninit1.nim b/tests/reject/tuninit1.nim
new file mode 100644
index 000000000..e51f9ce7c
--- /dev/null
+++ b/tests/reject/tuninit1.nim
@@ -0,0 +1,36 @@
+discard """
+  errormsg: "'y' might not have been initialized"
+  line:28
+"""
+
+import strutils
+
+{.warning[Uninit]:on.}
+
+proc p =
+  var x, y, z: int
+  if stdin.readLine == "true":
+    x = 34
+    
+    while false:
+      y = 999
+      break
+      
+    while true:
+      if x == 12: break
+      y = 9999
+      
+    try:
+      z = parseInt("1233")
+    except E_Base:
+      case x
+      of 34: z = 123
+      of 13: z = 34
+      else: z = 8
+  else:
+    y = 3444
+    x = 3111
+    z = 0
+  echo x, y, z
+  
+p()
diff --git a/todo.txt b/todo.txt
index 2aea8660f..9a3377d1c 100644
--- a/todo.txt
+++ b/todo.txt
@@ -2,7 +2,8 @@ version 0.9.4
 =============
 
 - make 'bind' default for templates and introduce 'mixin';
-- implement full 'not nil' checking; range[1..3] needs the same mechanism
+- test 'not nil' checking more
+- prove field accesses; prove array accesses
 - special rule for ``[]=``
 - ``=`` should be overloadable; requires specialization for ``=``; general
   lift mechanism in the compiler is already implemented for 'fields'
diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim
index 1e08e5653..982bfaf3d 100644
--- a/tools/niminst/debcreation.nim
+++ b/tools/niminst/debcreation.nim
@@ -227,9 +227,9 @@ when isMainModule:
   
   #echo(createRules())
 
-  prepDeb("nimrod", "0.8.14", "Dominik Picheta", "morfeusz8@gmail.com", 
+  prepDeb("nimrod", "0.9.2", "Dominik Picheta", "morfeusz8@gmail.com", 
     "The Nimrod compiler", "Compiler for the Nimrod programming language",
-    @[("bin/nimrod", "gpl2"), ("lib/*", "lgpl")], 
+    @[("bin/nimrod", "MIT"), ("lib/*", "MIT")], 
     @["bin/nimrod"], @["config/*"], @["doc/*"], @["lib/*"],
     "gcc (>= 4:4.3.2)", "gcc (>= 4:4.3.2)")
 
diff --git a/web/index.txt b/web/index.txt
index 260d303c8..e504b65c4 100644
--- a/web/index.txt
+++ b/web/index.txt
@@ -89,7 +89,7 @@ Nimrod plays nice with others
 * **The Nimrod Compiler can also generate C++ or Objective C for easier
   interfacing.**
 * There are lots of bindings: for example, bindings to GTK2, the Windows API, 
-  the POSIX API, OpenGL, SDL, Cario, Python, Lua, TCL, X11, libzip, PCRE, 
+  the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE, 
   libcurl, mySQL and SQLite are included in the standard distribution.
 * A C to Nimrod conversion utility: New bindings to C libraries are easily 
   generated by ``c2nim``.
diff --git a/web/news.txt b/web/news.txt
index 4f932bc05..f5737ffa3 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -36,7 +36,7 @@ Language Additions
 
 - Arrays can now be declared with a single integer literal ``N`` instead of a
   range; the range is then ``0..N-1``.
-
+- Added ``requiresInit`` pragma to enforce explicit initialization.
 
 
 2013-05-20 New website design!