summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/ccgtypes.nim18
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/guards.nim346
3 files changed, 356 insertions, 10 deletions
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 427b0ec86..88e7e264d 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -674,7 +674,7 @@ proc genProcHeader(m: BModule, prc: PSym): PRope =
 
 # ------------------ type info generation -------------------------------------
 
-proc genTypeInfo(m: BModule, typ: PType): PRope
+proc genTypeInfo(m: BModule, t: PType): PRope
 proc getNimNode(m: BModule): PRope = 
   result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)])
   inc(m.typeNodes)
@@ -692,7 +692,7 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} =
   result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
       (typ.sons[0] == nil) or isPureObject(typ))
 
-proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) = 
+proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) =
   var nimtypeKind: int
   #allocMemTI(m, typ, name)
   if isObjLackingTypeField(typ):
@@ -901,21 +901,21 @@ type
 
 include ccgtrav
 
-proc genTypeInfo(m: BModule, typ: PType): PRope = 
-  var t = getUniqueType(typ)
+proc genTypeInfo(m: BModule, t: PType): PRope = 
+  var t = getUniqueType(t)
   result = ropef("NTI$1", [toRope(t.id)])
-  let owner = typ.skipTypes(typedescPtrs).owner.getModule
+  if ContainsOrIncl(m.typeInfoMarker, t.id):
+    return con("(&".toRope, result, ")".toRope)
+  let owner = t.skipTypes(typedescPtrs).owner.getModule
   if owner != m.module:
     # make sure the type info is created in the owner module
-    discard genTypeInfo(owner.bmod, typ)
-    # refenrece the type info as extern here
+    discard genTypeInfo(owner.bmod, t)
+    # reference the type info as extern here
     discard cgsym(m, "TNimType")
     discard cgsym(m, "TNimNode")
     appf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n", 
          [result, toRope(typeToString(t))])
     return con("(&".toRope, result, ")".toRope)
-  if ContainsOrIncl(m.typeInfoMarker, t.id):
-    return con("(&".toRope, result, ")".toRope)
   case t.kind
   of tyEmpty: result = toRope"0"
   of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 7e782fc12..45fb9f878 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -1031,7 +1031,7 @@ proc genInitCode(m: BModule) =
       var procname = CStringLit(m.initProc, prc, m.module.name.s)
       app(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
     else:
-      app(prc, ~"\tvolatile TFrame F; F.len = 0;$N")
+      app(prc, ~"\tTFrame F; F.len = 0;$N")
     
   app(prc, genSectionStart(cpsInit))
   app(prc, m.preInitProc.s(cpsInit))
diff --git a/compiler/guards.nim b/compiler/guards.nim
new file mode 100644
index 000000000..6d0bf6bc6
--- /dev/null
+++ b/compiler/guards.nim
@@ -0,0 +1,346 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2013 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the 'implies' relation for guards.
+
+import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer
+
+const
+  someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, 
+    mEqUntracedRef, mEqStr, mEqSet, mEqCString}
+  
+  # set excluded here as the semantics are vastly different:
+  someLe = {mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,  
+            mLeCh, mLeB, mLePtr, mLeStr}
+  someLt = {mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum, 
+            mLtCh, mLtB, mLtPtr, mLtStr}
+
+  someLen = {mLengthOpenArray, mLengthStr, mLengthArray, mLengthSeq}
+
+  someIn = {mInRange, mInSet}
+
+proc isValue(n: PNode): bool = n.kind in {nkCharLit..nkNilLit}
+proc isLocation(n: PNode): bool = not n.isValue
+#n.kind in {nkSym, nkBracketExpr, nkDerefExpr, nkHiddenDeref, nkDotExpr}
+
+proc isLet(n: PNode): bool =
+  if n.kind == nkSym:
+    # XXX allow skResult, skVar here if not re-bound
+    if n.sym.kind in {skLet, skTemp, skForVar}:
+      result = true
+    elif n.sym.kind == skParam and skipTypes(n.sym.typ, 
+                                             abstractInst).kind != tyVar:
+      result = true
+
+proc isLetLocation(m: PNode): bool =
+  var n = m
+  while true:
+    case n.kind
+    of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
+      n = n.sons[0]
+    of nkBracketExpr:
+      if isConstExpr(n.sons[1]) or isLet(n.sons[1]):
+        n = n.sons[0]
+      else: return
+    of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+      n = n.sons[1]
+    else:
+      break
+  result = n.isLet
+
+proc neg(n: PNode): PNode =
+  if n.getMagic == mNot:
+    result = n.sons[1]
+  else:
+    result = newNodeI(nkCall, n.info, 2)
+    result.sons[0] = newSymNode(getSysMagic("not", mNot))
+    result.sons[1] = n
+
+proc usefulFact(n: PNode): PNode =
+  case n.getMagic
+  of someEq+someLe+someLt:
+    if isLetLocation(n.sons[1]) or n.len == 3 and isLetLocation(n.sons[2]):
+      # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
+      result = n
+  of someIn, mIsNil:
+    if isLetLocation(n.sons[1]):
+      result = n
+  of mAnd:
+    let
+      a = usefulFact(n.sons[1])
+      b = usefulFact(n.sons[2])
+    if a != nil and b != nil:
+      result = newNodeI(nkCall, n.info, 3)
+      result.sons[0] = newSymNode(getSysMagic("and", mAnd))
+      result.sons[1] = a
+      result.sons[2] = b
+    elif a != nil:
+      result = a
+    elif b != nil:
+      result = b
+  of mNot:
+    case n.sons[1].getMagic
+    of mNot:
+      # normalize 'not (not a)' into 'a':
+      result = usefulFact(n.sons[1].sons[1])
+    of mOr:
+      # not (a or b) --> not a and not b
+      let n = n.sons[1]
+      let
+        a = usefulFact(n.sons[1])
+        b = usefulFact(n.sons[2])
+      if a != nil and b != nil:
+        result = newNodeI(nkCall, n.info, 3)
+        result.sons[0] = newSymNode(getSysMagic("and", mAnd))
+        result.sons[1] = a.neg
+        result.sons[2] = b.neg
+    else:
+      let a = usefulFact(n.sons[1])
+      if a != nil: result = n
+  of mOr:
+    # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
+    # with that knowledge...
+    # DeMorgan helps a little though: 
+    #   not a or not b --> not (a and b)
+    #  (x == 3) or (y == 2)  ---> not ( not (x==3) and not (y == 2))
+    #  not (x != 3 and y != 2)
+    let
+      a = usefulFact(n.sons[1])
+      b = usefulFact(n.sons[2])
+    if a != nil and b != nil:
+      result = newNodeI(nkCall, n.info, 3)
+      result.sons[0] = newSymNode(getSysMagic("and", mAnd))
+      result.sons[1] = a.neg
+      result.sons[2] = b.neg
+      result = result.neg
+  elif n.kind == nkSym and n.sym.kind == skLet:
+    # consider:
+    #   let a = 2 < x
+    #   if a:
+    #     ...
+    # We make can easily replace 'a' by '2 < x' here:
+    result = usefulFact(n.sym.ast)
+  elif n.kind == nkStmtListExpr:
+    result = usefulFact(n.lastSon)
+
+type
+  TModel* = seq[PNode] # the "knowledge base"
+
+proc addFact*(m: var TModel, n: PNode) =
+  let n = usefulFact(n)
+  if n != nil: m.add n
+
+proc addFactNeg*(m: var TModel, n: PNode) = addFact(m, n.neg)
+
+proc sameTree(a, b: PNode): bool = 
+  result = false
+  if a == b:
+    result = true
+  elif (a != nil) and (b != nil) and (a.kind == b.kind):
+    case a.kind
+    of nkSym: result = a.sym == b.sym
+    of nkIdent: result = a.ident.id == b.ident.id
+    of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
+    of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
+    of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
+    of nkType: result = a.typ == b.typ
+    of nkEmpty, nkNilLit: result = true
+    else:
+      if sonsLen(a) == sonsLen(b):
+        for i in countup(0, sonsLen(a) - 1):
+          if not sameTree(a.sons[i], b.sons[i]): return
+        result = true
+
+proc valuesUnequal(a, b: PNode): bool =
+  if a.isValue and b.isValue:
+    result = not SameValue(a, b)
+
+type
+  TImplication* = enum
+    impUnknown, impNo, impYes  
+
+proc impliesEq(fact, eq: PNode): TImplication =
+  let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)
+  
+  case fact.sons[0].sym.magic
+  of someEq:
+    if sameTree(fact.sons[1], eq.sons[loc]):
+      # this is not correct; consider:  a == b;  a == 1 --> unknown!
+      if sameTree(fact.sons[2], eq.sons[val]): result = impYes
+      elif valuesUnequal(fact.sons[2], eq.sons[val]): result = impNo
+    elif sameTree(fact.sons[2], eq.sons[loc]):
+      if sameTree(fact.sons[1], eq.sons[val]): result = impYes
+      elif valuesUnequal(fact.sons[1], eq.sons[val]): result = impNo
+  of mInSet:
+    if sameTree(fact.sons[1], eq.sons[loc]) and isValue(eq.sons[val]):
+      if inSet(fact.sons[2], eq.sons[val]): result = impYes
+      else: result = impNo
+  of mIsNil:
+    if sameTree(fact.sons[1], eq.sons[loc]):
+      if eq.sons[val].kind == nkNilLit:
+        result = impYes
+  of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
+  else: nil
+  
+proc impliesIsNil(fact, eq: PNode): TImplication =
+  case fact.sons[0].sym.magic
+  of someEq:
+    if sameTree(fact.sons[1], eq.sons[1]):
+      if fact.sons[2].kind == nkNilLit: result = impYes
+    elif sameTree(fact.sons[2], eq.sons[1]):
+      if fact.sons[1].kind == nkNilLit: result = impYes
+  of mIsNil:
+    if sameTree(fact.sons[1], eq.sons[1]):
+      result = impYes
+  of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil")
+  else: nil
+
+proc pred(n: PNode): PNode =
+  if n.kind in {nkCharLit..nkUInt64Lit} and n.intVal != low(biggestInt):
+    result = copyNode(n)
+    dec result.intVal
+  else:
+    result = n
+
+proc impliesGe(fact, x, c: PNode): TImplication =
+  InternalAssert isLocation(x)
+  case fact.sons[0].sym.magic
+  of someEq:
+    if sameTree(fact.sons[1], x):
+      if isValue(fact.sons[2]) and isValue(c):
+        # fact:  x = 4;  question x >= 56? --> true iff 4 >= 56
+        if leValue(c, fact.sons[2]): result = impYes
+        else: result = impNo
+    elif sameTree(fact.sons[2], x):
+      if isValue(fact.sons[1]) and isValue(c):
+        if leValue(c, fact.sons[1]): result = impYes
+        else: result = impNo
+  of someLt:
+    if sameTree(fact.sons[1], x):
+      if isValue(fact.sons[2]) and isValue(c):
+        # fact:  x < 4;  question N <= x? --> false iff N <= 4
+        if leValue(fact.sons[2], c): result = impNo
+        # fact:  x < 4;  question 2 <= x? --> we don't know
+    elif sameTree(fact.sons[2], x):
+      # fact: 3 < x; question: N-1 < x ?  --> true iff N-1 <= 3
+      if isValue(fact.sons[1]) and isValue(c):
+        if leValue(c.pred, fact.sons[1]): result = impYes
+  of someLe:
+    if sameTree(fact.sons[1], x):
+      if isValue(fact.sons[2]) and isValue(c):
+        # fact:  x <= 4;  question x >= 56? --> false iff 4 <= 56
+        if leValue(fact.sons[2], c): result = impNo
+        # fact:  x <= 4;  question x >= 2? --> we don't know
+    elif sameTree(fact.sons[2], x):
+      # fact: 3 <= x; question: x >= 2 ?  --> true iff 2 <= 3
+      if isValue(fact.sons[1]) and isValue(c):
+        if leValue(c, fact.sons[1]): result = impYes
+  of mNot, mOr, mAnd: internalError(x.info, "impliesGe")
+  else: nil
+
+proc impliesLe(fact, x, c: PNode): TImplication =
+  if not isLocation(x):
+    return impliesGe(fact, c, x)
+  case fact.sons[0].sym.magic
+  of someEq:
+    if sameTree(fact.sons[1], x):
+      if isValue(fact.sons[2]) and isValue(c):
+        # fact:  x = 4;  question x <= 56? --> true iff 4 <= 56
+        if leValue(fact.sons[2], c): result = impYes
+        else: result = impNo
+    elif sameTree(fact.sons[2], x):
+      if isValue(fact.sons[1]) and isValue(c):
+        if leValue(fact.sons[1], c): result = impYes
+        else: result = impNo
+  of someLt:
+    if sameTree(fact.sons[1], x):
+      if isValue(fact.sons[2]) and isValue(c):
+        # fact:  x < 4;  question x <= N? --> true iff N-1 <= 4
+        if leValue(fact.sons[2], c.pred): result = impYes
+        # fact:  x < 4;  question x <= 2? --> we don't know
+    elif sameTree(fact.sons[2], x):
+      # fact: 3 < x; question: x <= 1 ?  --> false iff 1 <= 3
+      if isValue(fact.sons[1]) and isValue(c): 
+        if leValue(c, fact.sons[1]): result = impNo
+    
+  of someLe:
+    if sameTree(fact.sons[1], x):
+      if isValue(fact.sons[2]) and isValue(c):
+        # fact:  x <= 4;  question x <= 56? --> true iff 4 <= 56
+        if leValue(fact.sons[2], c): result = impYes
+        # fact:  x <= 4;  question x <= 2? --> we don't know
+    
+    elif sameTree(fact.sons[2], x):
+      # fact: 3 <= x; question: x <= 2 ?  --> false iff 2 < 3
+      if isValue(fact.sons[1]) and isValue(c): 
+        if leValue(c, fact.sons[1].pred): result = impNo
+
+  of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
+  else: nil
+
+proc impliesLt(fact, x, c: PNode): TImplication =
+  # x < 3  same as x <= 2:
+  let p = c.pred
+  if p != c:
+    result = impliesLe(fact, x, p)
+  else:
+    # 4 < x  same as 3 <= x
+    let q = x.pred
+    if q != x:
+      result = impliesLe(fact, q, c)
+
+proc factImplies(fact, prop: PNode, isNegation: bool): TImplication =
+  case fact.getMagic
+  of mNot:
+    case factImplies(fact.sons[1], prop, not isNegation)
+    of impUnknown: return impUnknown
+    of impNo: return impYes
+    of impYes: return impNo
+  of mAnd:
+    if not isNegation:
+      result = factImplies(fact.sons[1], prop, isNegation)
+      if result != impUnknown: return result
+      return factImplies(fact.sons[2], prop, isNegation)
+    else:
+      # careful!  not (a and b)  means  not a or not b:
+      # a or b --> both need to imply 'prop'
+      let a = factImplies(fact.sons[1], prop, isNegation)
+      let b = factImplies(fact.sons[2], prop, isNegation)
+      if a == b: return a
+      return impUnknown
+  else: discard
+  
+  case prop.sons[0].sym.magic
+  of mNot:
+    case fact.factImplies(prop.sons[1], isNegation)
+    of impUnknown: result = impUnknown
+    of impNo: result = impYes
+    of impYes: result = impNo
+  of mIsNil:
+    result = impliesIsNil(fact, prop)
+  of someEq:
+    result = impliesEq(fact, prop)
+  of someLe:
+    result = impliesLe(fact, prop.sons[1], prop.sons[2])
+  of someLt:
+    result = impliesLt(fact, prop.sons[1], prop.sons[2])
+  else:
+    internalError(prop.info, "invalid proposition")
+
+proc doesImply*(facts: TModel, prop: PNode): TImplication =
+  assert prop.kind in nkCallKinds
+  for f in facts:
+    result = f.factImplies(prop, false)
+    if result != impUnknown: return
+
+proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
+  var x = newNodeI(nkCall, arg.info, 2)
+  x.sons[0] = newSymNode(getSysMagic("isNil", mIsNil))
+  x.sons[1] = arg
+  result = doesImply(facts, x.neg)