summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim14
-rw-r--r--compiler/guards.nim7
-rw-r--r--compiler/sempass2.nim57
-rw-r--r--compiler/semstmts.nim8
-rw-r--r--compiler/semtypes.nim1
-rw-r--r--compiler/sigmatch.nim31
-rw-r--r--tests/manyloc/keineschweine/lib/sg_packets.nim6
-rw-r--r--todo.txt1
-rw-r--r--web/news.txt1
9 files changed, 99 insertions, 27 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index f4b1b84f5..e4f7f6d98 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -1175,10 +1175,18 @@ proc newSons(father: PNode, length: int) =
     setlen(father.sons, length)
 
 proc propagateToOwner*(owner, elem: PType) =
-  owner.flags = owner.flags + (elem.flags * {tfNeedsInit, tfHasShared, 
-                                             tfHasMeta, tfHasGCedMem})
+  const HaveTheirOwnEmpty =  {tySequence, tySet}
+  owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta,
+                                             tfHasGCedMem})
   if tfNotNil in elem.flags:
-    owner.flags.incl tfNeedsInit
+    if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}:
+      owner.flags.incl tfNotNil
+    elif owner.kind notin HaveTheirOwnEmpty:
+      owner.flags.incl tfNeedsInit
+  
+  if tfNeedsInit in elem.flags:
+    if owner.kind in HaveTheirOwnEmpty: nil
+    else: owner.flags.incl tfNeedsInit
     
   if tfShared in elem.flags:
     owner.flags.incl tfHasShared
diff --git a/compiler/guards.nim b/compiler/guards.nim
index d4cb34f36..aece63b19 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -532,6 +532,13 @@ proc buildElse(n: PNode): PNode =
   result.sons[1] = s
   result.sons[2] = n.sons[0]
 
+proc addDiscriminantFact*(m: var TModel, n: PNode) =
+  var fact = newNodeI(nkCall, n.info, 3)
+  fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
+  fact.sons[1] = n.sons[0]
+  fact.sons[2] = n.sons[1]
+  m.add fact
+
 proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
   let branch = n.sons[i]
   if branch.kind == nkOfBranch:
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 50286d399..2c87c3b2c 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -74,7 +74,6 @@ type
     bottom: int
     owner: PSym
     init: seq[int] # list of initialized variables
-                   # coming soon: "guard" tracking for 'let' variables
     guards: TModel # nested guards
   PEffects = var TEffects
 
@@ -89,11 +88,19 @@ proc initVar(a: PEffects, n: PNode) =
       if x == s.id: return
     a.init.add s.id
 
+proc initVarViaNew(a: PEffects, n: PNode) =
+  if n.kind != nkSym: return
+  let s = n.sym
+  if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}:
+    # 'x' is not nil, but that doesn't mean it's not nil children
+    # are initialized:
+    initVarViaNew(a, n)
+
 proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if isLocalVar(a, s):
     if s.id notin a.init:
-      if tfNeedsInit in s.typ.flags:
+      if {tfNeedsInit, tfNotNil} * s.typ.flags != {}:
         when true:
           Message(n.info, warnProveInit, s.name.s)
         else:
@@ -298,6 +305,18 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let tagSpec = effectSpec(pragma, wTags)
   mergeTags(tracked, tagSpec, n)
 
+proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
+  let n = n.skipConv
+  if paramType != nil and tfNotNil in paramType.flags and 
+      n.typ != nil and tfNotNil notin n.typ.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 trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
   let op = n.typ
   if op != nil and op.kind == tyProc and n.kind != nkNilLit:
@@ -315,15 +334,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
     else:
       mergeEffects(tracked, effectList.sons[exceptionEffects], n)
       mergeTags(tracked, effectList.sons[tagEffects], n)
-  if paramType != nil:
-    if 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
+  notNilCheck(tracked, n, paramType)
 
 proc breaksBlock(n: PNode): bool =
   case n.kind
@@ -456,8 +467,7 @@ proc track(tracked: PEffects, n: PNode) =
     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!
+      initVarViaNew(tracked, n.sons[1])
     for i in 0 .. <safeLen(n):
       track(tracked, n.sons[i])
   of nkCheckedFieldExpr:
@@ -471,11 +481,17 @@ proc track(tracked: PEffects, n: PNode) =
     initVar(tracked, n.sons[0])
     invalidateFacts(tracked.guards, n.sons[0])
     track(tracked, n.sons[0])
+    notNilCheck(tracked, n.sons[1], n.sons[0].typ)
   of nkVarSection:
     for child in n:
-      if child.kind == nkIdentDefs and lastSon(child).kind != nkEmpty:
-        track(tracked, lastSon(child))
-        for i in 0 .. child.len-3: initVar(tracked, child.sons[i])
+      let last = lastSon(child)
+      if child.kind == nkIdentDefs and last.kind != nkEmpty:
+        track(tracked, last)
+        for i in 0 .. child.len-3:
+          initVar(tracked, child.sons[i])
+          notNilCheck(tracked, last, child.sons[i].typ)
+      # since 'var (a, b): T = ()' is not even allowed, there is always type
+      # inference for (a, b) and thus no nil checking is necessary.
   of nkCaseStmt: trackCase(tracked, n)
   of nkIfStmt, nkIfExpr: trackIf(tracked, n)
   of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n.sons[1])
@@ -498,6 +514,15 @@ proc track(tracked: PEffects, n: PNode) =
     for i in 0 .. <len(n):
       track(tracked, n.sons[i])
     setLen(tracked.init, oldState)
+  of nkObjConstr:
+    track(tracked, n.sons[0])
+    let oldFacts = tracked.guards.len
+    for i in 1 .. <len(n):
+      let x = n.sons[i]
+      track(tracked, x)
+      if sfDiscriminant in x.sons[0].sym.flags:
+        addDiscriminantFact(tracked.guards, x)
+    setLen(tracked.guards, oldFacts)
   else:
     for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
 
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 75ab9920d..47b591d83 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -315,6 +315,13 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
     result = semIdentWithPragma(c, kind, n, {})
   suggestSym(n, result)
 
+proc checkNilable(v: PSym) =
+  if sfGlobal in v.flags and {tfNotNil, tfNeedsInit} * v.typ.flags != {}:
+    if v.ast.isNil:
+      Message(v.info, warnProveInit, v.name.s)
+    elif tfNotNil in v.typ.flags and tfNotNil notin v.ast.typ.flags:
+      Message(v.info, warnProveInit, v.name.s)
+
 proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = 
   var b: PNode
   result = copyNode(n)
@@ -390,6 +397,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       else: 
         v.typ = tup.sons[j]
         b.sons[j] = newSymNode(v)
+      checkNilable(v)
     
 proc semConst(c: PContext, n: PNode): PNode = 
   result = copyNode(n)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 769e2ab7d..7efa207a8 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -865,7 +865,6 @@ 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/sigmatch.nim b/compiler/sigmatch.nim
index b90e73a64..6fb250179 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -47,6 +47,9 @@ type
     isGeneric,
     isFromIntLit,            # conversion *from* int literal; proven safe
     isEqual
+  
+const
+  isNilConversion = isConvertible # maybe 'isIntConv' fits better?
     
 proc markUsed*(n: PNode, s: PSym)
 
@@ -471,6 +474,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       else:
         result = typeRel(c, f.sons[0], a.sons[0])
         if result < isGeneric: result = isNone
+        elif tfNotNil in f.flags and tfNotNil notin a.flags:
+          result = isNilConversion
     of tyNil: result = f.allowsNil
     else: nil
   of tyOrdinal:
@@ -506,6 +511,8 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     of tyPtr: 
       result = typeRel(c, base(f), base(a))
       if result <= isConvertible: result = isNone
+      elif tfNotNil in f.flags and tfNotNil notin a.flags:
+        result = isNilConversion
     of tyNil: result = f.allowsNil
     else: nil
   of tyRef: 
@@ -513,13 +520,21 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     of tyRef:
       result = typeRel(c, base(f), base(a))
       if result <= isConvertible: result = isNone
+      elif tfNotNil in f.flags and tfNotNil notin a.flags:
+        result = isNilConversion
     of tyNil: result = f.allowsNil
     else: nil
   of tyProc:
     result = procTypeRel(c, f, a)
-  of tyPointer: 
+    if result != isNone and tfNotNil in f.flags and tfNotNil notin a.flags:
+      result = isNilConversion
+  of tyPointer:
     case a.kind
-    of tyPointer: result = isEqual
+    of tyPointer:
+      if tfNotNil in f.flags and tfNotNil notin a.flags:
+        result = isNilConversion
+      else:
+        result = isEqual
     of tyNil: result = f.allowsNil
     of tyProc:
       if a.callConv != ccClosure: result = isConvertible
@@ -527,13 +542,21 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     else: nil
   of tyString: 
     case a.kind
-    of tyString: result = isEqual
+    of tyString: 
+      if tfNotNil in f.flags and tfNotNil notin a.flags:
+        result = isNilConversion
+      else:
+        result = isEqual
     of tyNil: result = f.allowsNil
     else: nil
   of tyCString:
     # conversion from string to cstring is automatic:
     case a.Kind
-    of tyCString: result = isEqual
+    of tyCString:
+      if tfNotNil in f.flags and tfNotNil notin a.flags:
+        result = isNilConversion
+      else:
+        result = isEqual
     of tyNil: result = f.allowsNil
     of tyString: result = isConvertible
     of tyPtr:
diff --git a/tests/manyloc/keineschweine/lib/sg_packets.nim b/tests/manyloc/keineschweine/lib/sg_packets.nim
index 30e83c147..625436cb6 100644
--- a/tests/manyloc/keineschweine/lib/sg_packets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_packets.nim
@@ -55,7 +55,8 @@ idPacket(ZoneQuery, 'Q',
   tuple[pad: char = '\0'])
 
 type SpawnKind = enum
-  SpawnItem = 1'i8, SpawnVehicle, SpawnObject
+  SpawnDummy,
+  SpawnItem, SpawnVehicle, SpawnObject
 forwardPacketT(SpawnKind, int8)
 defPacket(ScSpawn, tuple[
   kind: SpawnKind; id: uint16; record: uint16; amount: uint16])
@@ -64,7 +65,8 @@ defPacket(ScSpawn, tuple[
 
 
 type TAssetType* = enum
-  FZoneCfg = 1'i8, FGraphics, FSound 
+  FDummy, 
+  FZoneCfg, FGraphics, FSound 
 
 forwardPacketT(TAssetType, int8)
 forwardPacket(MD5Digest, array[0..15, int8])
diff --git a/todo.txt b/todo.txt
index 83af59b87..4bdd004cd 100644
--- a/todo.txt
+++ b/todo.txt
@@ -2,7 +2,6 @@ version 0.9.4
 =============
 
 - make 'bind' default for templates and introduce 'mixin'
-- 'not nil' checking for globals
 - prove array accesses
 - special rule for ``[]=``
 - ``=`` should be overloadable; requires specialization for ``=``; general
diff --git a/web/news.txt b/web/news.txt
index 98d3298b9..fdbb156a7 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -29,6 +29,7 @@ Compiler Additions
 - The compiler can now warn about "uninitialized" variables. (There are no
   real uninitialized variables in Nimrod as they are initialized to binary
   zero). Activate via ``{.warning[Uninit]:on.}``.
+- The compiler now enforces the ``not nil`` constraint.
 
 
 Language Additions