This commit is contained in:
Araq 2014-02-14 23:47:06 +01:00
commit 2b9311e9f1
5 changed files with 86 additions and 27 deletions

View file

@ -9,7 +9,7 @@
## This module implements the 'implies' relation for guards. ## This module implements the 'implies' relation for guards.
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents
const const
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@ -69,9 +69,23 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode = proc getMagicOp(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
let
opLe = getMagicOp("<=", mLeI)
opLt = getMagicOp("<", mLtI)
opAnd = getMagicOp("and", mAnd)
opOr = getMagicOp("or", mOr)
opNot = getMagicOp("not", mNot)
opIsNil = getMagicOp("isnil", mIsNil)
opContains = getMagicOp("contains", mInSet)
opEq = getMagicOp("==", mEqI)
proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3) result = newNodeI(nkCall, fact.info, 3)
result.sons[0] = newSymNode(getSysMagic(newOp, m)) result.sons[0] = newSymNode(newOp)
result.sons[1] = fact.sons[2] result.sons[1] = fact.sons[2]
result.sons[2] = fact.sons[1] result.sons[2] = fact.sons[1]
@ -82,9 +96,9 @@ proc neg(n: PNode): PNode =
result = n.sons[1] result = n.sons[1]
of someLt: of someLt:
# not (a < b) == a >= b == b <= a # not (a < b) == a >= b == b <= a
result = swapArgs(n, "<=", mLeI) result = swapArgs(n, opLe)
of someLe: of someLe:
result = swapArgs(n, "<", mLtI) result = swapArgs(n, opLt)
of mInSet: of mInSet:
if n.sons[1].kind != nkCurly: return nil if n.sons[1].kind != nkCurly: return nil
let t = n.sons[2].typ.skipTypes(abstractInst) let t = n.sons[2].typ.skipTypes(abstractInst)
@ -110,7 +124,7 @@ proc neg(n: PNode): PNode =
b = n.sons[2].neg b = n.sons[2].neg
if a != nil and b != nil: if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd)) result.sons[0] = newSymNode(opAnd)
result.sons[1] = a result.sons[1] = a
result.sons[2] = b result.sons[2] = b
elif a != nil: elif a != nil:
@ -120,12 +134,12 @@ proc neg(n: PNode): PNode =
else: else:
# leave not (a == 4) as it is # leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2) result = newNodeI(nkCall, n.info, 2)
result.sons[0] = newSymNode(getSysMagic("not", mNot)) result.sons[0] = newSymNode(opNot)
result.sons[1] = n result.sons[1] = n
proc buildIsNil(arg: PNode): PNode = proc buildIsNil(arg: PNode): PNode =
result = newNodeI(nkCall, arg.info, 2) result = newNodeI(nkCall, arg.info, 2)
result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil)) result.sons[0] = newSymNode(opIsNil)
result.sons[1] = arg result.sons[1] = arg
proc usefulFact(n: PNode): PNode = proc usefulFact(n: PNode): PNode =
@ -154,7 +168,7 @@ proc usefulFact(n: PNode): PNode =
b = usefulFact(n.sons[2]) b = usefulFact(n.sons[2])
if a != nil and b != nil: if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd)) result.sons[0] = newSymNode(opAnd)
result.sons[1] = a result.sons[1] = a
result.sons[2] = b result.sons[2] = b
elif a != nil: elif a != nil:
@ -177,7 +191,7 @@ proc usefulFact(n: PNode): PNode =
b = usefulFact(n.sons[2]).neg b = usefulFact(n.sons[2]).neg
if a != nil and b != nil: if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("and", mAnd)) result.sons[0] = newSymNode(opAnd)
result.sons[1] = a result.sons[1] = a
result.sons[2] = b result.sons[2] = b
result = result.neg result = result.neg
@ -520,7 +534,7 @@ proc buildOf(it, loc: PNode): PNode =
s.typ = settype(loc) s.typ = settype(loc)
for i in 0..it.len-2: s.sons[i] = it.sons[i] for i in 0..it.len-2: s.sons[i] = it.sons[i]
result = newNodeI(nkCall, it.info, 3) result = newNodeI(nkCall, it.info, 3)
result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) result.sons[0] = newSymNode(opContains)
result.sons[1] = s result.sons[1] = s
result.sons[2] = loc result.sons[2] = loc
@ -532,20 +546,20 @@ proc buildElse(n: PNode): PNode =
for j in 0..branch.len-2: for j in 0..branch.len-2:
s.add(branch.sons[j]) s.add(branch.sons[j])
result = newNodeI(nkCall, n.info, 3) result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) result.sons[0] = newSymNode(opContains)
result.sons[1] = s result.sons[1] = s
result.sons[2] = n.sons[0] result.sons[2] = n.sons[0]
proc addDiscriminantFact*(m: var TModel, n: PNode) = proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3) var fact = newNodeI(nkCall, n.info, 3)
fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) fact.sons[0] = newSymNode(opEq)
fact.sons[1] = n.sons[0] fact.sons[1] = n.sons[0]
fact.sons[2] = n.sons[1] fact.sons[2] = n.sons[1]
m.add fact m.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) = proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3) var fact = newNodeI(nkCall, key.info, 3)
fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) fact.sons[0] = newSymNode(opEq)
fact.sons[1] = key fact.sons[1] = key
fact.sons[2] = value fact.sons[2] = value
m.add fact m.add fact

View file

@ -84,10 +84,10 @@ proc initVar(a: PEffects, n: PNode) =
proc initVarViaNew(a: PEffects, n: PNode) = proc initVarViaNew(a: PEffects, n: PNode) =
if n.kind != nkSym: return if n.kind != nkSym: return
let s = n.sym let s = n.sym
if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}: if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
# 'x' is not nil, but that doesn't mean it's not nil children # 'x' is not nil, but that doesn't mean it's not nil children
# are initialized: # are initialized:
initVarViaNew(a, n) initVar(a, n)
proc useVar(a: PEffects, n: PNode) = proc useVar(a: PEffects, n: PNode) =
let s = n.sym let s = n.sym
@ -466,8 +466,7 @@ proc track(tracked: PEffects, n: PNode) =
mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, 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, if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
mNewSeq, mShallowCopy}:
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
initVarViaNew(tracked, n.sons[1]) initVarViaNew(tracked, n.sons[1])
for i in 0 .. <safeLen(n): for i in 0 .. <safeLen(n):
@ -581,22 +580,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
if not isNil(tagsSpec): if not isNil(tagsSpec):
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
t.exc = effects.sons[exceptionEffects]
t.tags = effects.sons[tagEffects]
t.owner = s
t.init = @[]
t.guards = @[]
proc trackProc*(s: PSym, body: PNode) = proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0] var effects = s.typ.n.sons[0]
internalAssert effects.kind == nkEffectList internalAssert effects.kind == nkEffectList
# effects already computed? # effects already computed?
if sfForward in s.flags: return if sfForward in s.flags: return
if effects.len == effectListLen: return if effects.len == effectListLen: return
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = newNodeI(nkArgList, body.info)
effects.sons[tagEffects] = newNodeI(nkArgList, body.info)
var t: TEffects var t: TEffects
t.exc = effects.sons[exceptionEffects] initEffects(effects, s, t)
t.tags = effects.sons[tagEffects]
t.owner = s
t.init = @[]
t.guards = @[]
track(t, body) track(t, body)
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
@ -619,3 +622,9 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec: # after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec effects.sons[tagEffects] = tagsSpec
proc trackTopLevelStmt*(module: PSym; n: PNode) =
var effects = newNode(nkEffectList, n.info)
var t: TEffects
initEffects(effects, module, t)
track(t, n)

View file

@ -754,6 +754,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
result = processTransf(c, n, module) result = processTransf(c, n, module)
result = liftLambdasForTopLevel(module, result) result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
when useEffectSystem: trackTopLevelStmt(module, result)
proc transformExpr*(module: PSym, n: PNode): PNode = proc transformExpr*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags: if nfTransf in n.flags:

View file

@ -4,7 +4,7 @@ type
PMenuItem = ref object PMenuItem = ref object
proc createMenuItem*(menu: PMenu, label: string, proc createMenuItem*(menu: PMenu, label: string,
action: proc (i: PMenuItem, p: pointer) {.cdecl.}) = nil action: proc (i: PMenuItem, p: pointer) {.cdecl.}) = discard
var s: PMenu var s: PMenu
createMenuItem(s, "Go to definition...", createMenuItem(s, "Go to definition...",

35
tests/notnil/tnotnil3.nim Normal file
View file

@ -0,0 +1,35 @@
discard """
errormsg: "cannot prove 'variable' is not nil"
line: 31
"""
# bug #584
# Testprogram for 'not nil' check
const testWithResult = true
type
A = object
B = object
C = object
a: ref A
b: ref B
proc testNotNil(c: ref C not nil) =
discard
when testWithResult:
proc testNotNilOnResult(): ref C =
new(result)
#result.testNotNil() # Here 'not nil' can't be proved
var variable: ref C
new(variable)
variable.testNotNil() # Here 'not nil' is proved
when testWithResult:
discard testNotNilOnResult()