fixes #584
This commit is contained in:
parent
cf544f6a85
commit
2b9311e9f1
5 changed files with 86 additions and 27 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
35
tests/notnil/tnotnil3.nim
Normal 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()
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue