mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-30 09:54:49 +00:00
improvements for 'not nil' checking
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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])
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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])
|
||||
|
||||
1
todo.txt
1
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user