sempass2 compiles again
This commit is contained in:
parent
7eb39d9d26
commit
2e76a92fe1
3 changed files with 85 additions and 81 deletions
|
|
@ -280,7 +280,7 @@ errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
|
||||||
errWrongNumberOfArguments: "wrong number of arguments",
|
errWrongNumberOfArguments: "wrong number of arguments",
|
||||||
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
|
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
|
||||||
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
|
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
|
||||||
errXCannotBePassedToProcVar: "'$1' cannot be passed to a procvar",
|
errXCannotBePassedToProcVar: ,
|
||||||
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1",
|
||||||
errImplOfXNotAllowed: "implementation of '$1' is not allowed",
|
errImplOfXNotAllowed: "implementation of '$1' is not allowed",
|
||||||
errImplOfXexpected: ,
|
errImplOfXexpected: ,
|
||||||
|
|
|
||||||
|
|
@ -402,8 +402,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
|
||||||
|
|
||||||
type
|
type
|
||||||
TModel* = object
|
TModel* = object
|
||||||
s: seq[PNode] # the "knowledge base"
|
s*: seq[PNode] # the "knowledge base"
|
||||||
o: Operators
|
o*: Operators
|
||||||
|
|
||||||
proc addFact*(m: var TModel, nn: PNode) =
|
proc addFact*(m: var TModel, nn: PNode) =
|
||||||
let n = usefulFact(nn, m.o)
|
let n = usefulFact(nn, m.o)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||||
wordrecg, strutils, options, guards, writetracking, configuration
|
wordrecg, strutils, options, guards, writetracking, configuration,
|
||||||
|
modulegraphs
|
||||||
|
|
||||||
when defined(useDfa):
|
when defined(useDfa):
|
||||||
import dfa
|
import dfa
|
||||||
|
|
@ -53,6 +54,7 @@ type
|
||||||
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
|
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||||
maxLockLevel, currLockLevel: TLockLevel
|
maxLockLevel, currLockLevel: TLockLevel
|
||||||
config: ConfigRef
|
config: ConfigRef
|
||||||
|
graph: ModuleGraph
|
||||||
PEffects = var TEffects
|
PEffects = var TEffects
|
||||||
|
|
||||||
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
||||||
|
|
@ -257,32 +259,29 @@ proc addToIntersection(inter: var TIntersection, s: int) =
|
||||||
proc throws(tracked, n: PNode) =
|
proc throws(tracked, n: PNode) =
|
||||||
if n.typ == nil or n.typ.kind != tyError: tracked.add n
|
if n.typ == nil or n.typ.kind != tyError: tracked.add n
|
||||||
|
|
||||||
proc getEbase(g: ModuleGraph): PType =
|
proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
|
||||||
result = g.sysTypeFromName"Exception"
|
result = g.sysTypeFromName(info, "Exception")
|
||||||
|
|
||||||
proc excType(n: PNode): PType =
|
proc excType(g: ModuleGraph; n: PNode): PType =
|
||||||
# reraise is like raising E_Base:
|
# reraise is like raising E_Base:
|
||||||
let t = if n.kind == nkEmpty or n.typ.isNil: getEbase() else: n.typ
|
let t = if n.kind == nkEmpty or n.typ.isNil: getEbase(g, n.info) else: n.typ
|
||||||
result = skipTypes(t, skipPtrs)
|
result = skipTypes(t, skipPtrs)
|
||||||
|
|
||||||
proc createRaise(n: PNode): PNode =
|
proc createRaise(g: ModuleGraph; n: PNode): PNode =
|
||||||
result = newNode(nkType)
|
result = newNode(nkType)
|
||||||
result.typ = getEbase()
|
result.typ = getEbase(g, n.info)
|
||||||
if not n.isNil: result.info = n.info
|
if not n.isNil: result.info = n.info
|
||||||
|
|
||||||
proc createTag(n: PNode): PNode =
|
proc createTag(g: ModuleGraph; n: PNode): PNode =
|
||||||
result = newNode(nkType)
|
result = newNode(nkType)
|
||||||
if getCompilerProc("RootEffect") != nil:
|
result.typ = g.sysTypeFromName(n.info, "RootEffect")
|
||||||
result.typ = sysTypeFromName"RootEffect"
|
|
||||||
else:
|
|
||||||
result.typ = sysTypeFromName"TEffect"
|
|
||||||
if not n.isNil: result.info = n.info
|
if not n.isNil: result.info = n.info
|
||||||
|
|
||||||
proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
|
proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
|
||||||
assert e.kind != nkRaiseStmt
|
assert e.kind != nkRaiseStmt
|
||||||
var aa = a.exc
|
var aa = a.exc
|
||||||
for i in a.bottom ..< aa.len:
|
for i in a.bottom ..< aa.len:
|
||||||
if sameType(aa[i].excType, e.excType):
|
if sameType(a.graph.excType(aa[i]), a.graph.excType(e)):
|
||||||
if not useLineInfo or gCmd == cmdDoc: return
|
if not useLineInfo or gCmd == cmdDoc: return
|
||||||
elif aa[i].info == e.info: return
|
elif aa[i].info == e.info: return
|
||||||
throws(a.exc, e)
|
throws(a.exc, e)
|
||||||
|
|
@ -297,19 +296,19 @@ proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
|
||||||
|
|
||||||
proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
|
proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
|
||||||
if b.isNil:
|
if b.isNil:
|
||||||
addEffect(a, createRaise(comesFrom))
|
addEffect(a, createRaise(a.graph, comesFrom))
|
||||||
else:
|
else:
|
||||||
for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
|
for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
|
||||||
|
|
||||||
proc mergeTags(a: PEffects, b, comesFrom: PNode) =
|
proc mergeTags(a: PEffects, b, comesFrom: PNode) =
|
||||||
if b.isNil:
|
if b.isNil:
|
||||||
addTag(a, createTag(comesFrom))
|
addTag(a, createTag(a.graph, comesFrom))
|
||||||
else:
|
else:
|
||||||
for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
|
for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
|
||||||
|
|
||||||
proc listEffects(a: PEffects) =
|
proc listEffects(a: PEffects) =
|
||||||
for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ))
|
for e in items(a.exc): message(a.config, e.info, hintUser, typeToString(e.typ))
|
||||||
for e in items(a.tags): message(e.info, hintUser, typeToString(e.typ))
|
for e in items(a.tags): message(a.config, e.info, hintUser, typeToString(e.typ))
|
||||||
#if a.maxLockLevel != 0:
|
#if a.maxLockLevel != 0:
|
||||||
# message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
|
# message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
|
||||||
|
|
||||||
|
|
@ -319,7 +318,7 @@ proc catches(tracked: PEffects, e: PType) =
|
||||||
var i = tracked.bottom
|
var i = tracked.bottom
|
||||||
while i < L:
|
while i < L:
|
||||||
# r supertype of e?
|
# r supertype of e?
|
||||||
if safeInheritanceDiff(tracked.exc[i].excType, e) <= 0:
|
if safeInheritanceDiff(tracked.graph.excType(tracked.exc[i]), e) <= 0:
|
||||||
tracked.exc.sons[i] = tracked.exc.sons[L-1]
|
tracked.exc.sons[i] = tracked.exc.sons[L-1]
|
||||||
dec L
|
dec L
|
||||||
else:
|
else:
|
||||||
|
|
@ -488,7 +487,7 @@ proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
|
||||||
if lockLevel >= tracked.currLockLevel:
|
if lockLevel >= tracked.currLockLevel:
|
||||||
# if in lock section:
|
# if in lock section:
|
||||||
if tracked.currLockLevel > 0.TLockLevel:
|
if tracked.currLockLevel > 0.TLockLevel:
|
||||||
localError n.info, errGenerated,
|
localError tracked.config, n.info, errGenerated,
|
||||||
"expected lock level < " & $tracked.currLockLevel &
|
"expected lock level < " & $tracked.currLockLevel &
|
||||||
" but got lock level " & $lockLevel
|
" but got lock level " & $lockLevel
|
||||||
tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
|
tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
|
||||||
|
|
@ -502,22 +501,22 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||||
mergeTags(tracked, tagSpec, n)
|
mergeTags(tracked, tagSpec, n)
|
||||||
|
|
||||||
if notGcSafe(s.typ) and sfImportc notin s.flags:
|
if notGcSafe(s.typ) and sfImportc notin s.flags:
|
||||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
|
||||||
markGcUnsafe(tracked, s)
|
markGcUnsafe(tracked, s)
|
||||||
if tfNoSideEffect notin s.typ.flags:
|
if tfNoSideEffect notin s.typ.flags:
|
||||||
markSideEffect(tracked, s)
|
markSideEffect(tracked, s)
|
||||||
mergeLockLevels(tracked, n, s.getLockLevel)
|
mergeLockLevels(tracked, n, s.getLockLevel)
|
||||||
|
|
||||||
proc procVarcheck(n: PNode) =
|
proc procVarcheck(n: PNode; conf: ConfigRef) =
|
||||||
if n.kind in nkSymChoices:
|
if n.kind in nkSymChoices:
|
||||||
for x in n: procVarCheck(x)
|
for x in n: procVarCheck(x, conf)
|
||||||
elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
|
elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
|
||||||
localError(n.info, errXCannotBePassedToProcVar, n.sym.name.s)
|
localError(conf, n.info, "'$1' cannot be passed to a procvar" % n.sym.name.s)
|
||||||
|
|
||||||
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
let n = n.skipConv
|
let n = n.skipConv
|
||||||
if paramType.isNil or paramType.kind != tyTypeDesc:
|
if paramType.isNil or paramType.kind != tyTypeDesc:
|
||||||
procVarcheck skipConvAndClosure(n)
|
procVarcheck skipConvAndClosure(n), tracked.config
|
||||||
#elif n.kind in nkSymChoices:
|
#elif n.kind in nkSymChoices:
|
||||||
# echo "came here"
|
# echo "came here"
|
||||||
let paramType = paramType.skipTypesOrNil(abstractInst)
|
let paramType = paramType.skipTypesOrNil(abstractInst)
|
||||||
|
|
@ -533,15 +532,16 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
return
|
return
|
||||||
case impliesNotNil(tracked.guards, n)
|
case impliesNotNil(tracked.guards, n)
|
||||||
of impUnknown:
|
of impUnknown:
|
||||||
message(n.info, errGenerated,
|
message(tracked.config, n.info, errGenerated,
|
||||||
"cannot prove '$1' is not nil" % n.renderTree)
|
"cannot prove '$1' is not nil" % n.renderTree)
|
||||||
of impNo:
|
of impNo:
|
||||||
message(n.info, errGenerated, "'$1' is provably nil" % n.renderTree)
|
message(tracked.config, n.info, errGenerated,
|
||||||
|
"'$1' is provably nil" % n.renderTree)
|
||||||
of impYes: discard
|
of impYes: discard
|
||||||
|
|
||||||
proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
|
proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
|
||||||
addEffect(tracked, createRaise(n))
|
addEffect(tracked, createRaise(tracked.graph, n))
|
||||||
addTag(tracked, createTag(n))
|
addTag(tracked, createTag(tracked.graph, n))
|
||||||
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
|
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
|
||||||
else: op.lockLevel
|
else: op.lockLevel
|
||||||
#if lockLevel == UnknownLockLevel:
|
#if lockLevel == UnknownLockLevel:
|
||||||
|
|
@ -556,7 +556,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
let a = skipConvAndClosure(n)
|
let a = skipConvAndClosure(n)
|
||||||
let op = a.typ
|
let op = a.typ
|
||||||
if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
|
if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit:
|
||||||
internalAssert op.n.sons[0].kind == nkEffectList
|
internalAssert tracked.config, op.n.sons[0].kind == nkEffectList
|
||||||
var effectList = op.n.sons[0]
|
var effectList = op.n.sons[0]
|
||||||
let s = n.skipConv
|
let s = n.skipConv
|
||||||
if s.kind == nkSym and s.sym.kind in routineKinds:
|
if s.kind == nkSym and s.sym.kind in routineKinds:
|
||||||
|
|
@ -571,7 +571,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
assumeTheWorst(tracked, n, op)
|
assumeTheWorst(tracked, n, op)
|
||||||
# assume GcUnsafe unless in its type; 'forward' does not matter:
|
# assume GcUnsafe unless in its type; 'forward' does not matter:
|
||||||
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
|
if notGcSafe(op) and not isOwnedProcVar(a, tracked.owner):
|
||||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
|
||||||
markGcUnsafe(tracked, a)
|
markGcUnsafe(tracked, a)
|
||||||
elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
|
elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
|
||||||
markSideEffect(tracked, a)
|
markSideEffect(tracked, a)
|
||||||
|
|
@ -579,7 +579,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||||
if notGcSafe(op):
|
if notGcSafe(op):
|
||||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
|
||||||
markGcUnsafe(tracked, a)
|
markGcUnsafe(tracked, a)
|
||||||
elif tfNoSideEffect notin op.flags:
|
elif tfNoSideEffect notin op.flags:
|
||||||
markSideEffect(tracked, a)
|
markSideEffect(tracked, a)
|
||||||
|
|
@ -591,7 +591,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
# XXX figure out why this can be a non tyProc here. See httpclient.nim for an
|
# XXX figure out why this can be a non tyProc here. See httpclient.nim for an
|
||||||
# example that triggers it.
|
# example that triggers it.
|
||||||
if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
|
if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
|
||||||
localError(n.info, $n & " is not GC safe")
|
localError(tracked.config, n.info, $n & " is not GC safe")
|
||||||
notNilCheck(tracked, n, paramType)
|
notNilCheck(tracked, n, paramType)
|
||||||
|
|
||||||
proc breaksBlock(n: PNode): bool =
|
proc breaksBlock(n: PNode): bool =
|
||||||
|
|
@ -607,18 +607,18 @@ proc breaksBlock(n: PNode): bool =
|
||||||
proc trackCase(tracked: PEffects, n: PNode) =
|
proc trackCase(tracked: PEffects, n: PNode) =
|
||||||
track(tracked, n.sons[0])
|
track(tracked, n.sons[0])
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
let oldFacts = tracked.guards.len
|
let oldFacts = tracked.guards.s.len
|
||||||
let stringCase = skipTypes(n.sons[0].typ,
|
let stringCase = skipTypes(n.sons[0].typ,
|
||||||
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
|
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
|
||||||
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
|
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
|
||||||
warnProveField in gNotes
|
warnProveField in tracked.config.notes
|
||||||
var inter: TIntersection = @[]
|
var inter: TIntersection = @[]
|
||||||
var toCover = 0
|
var toCover = 0
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
if interesting:
|
if interesting:
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
addCaseBranchFacts(tracked.guards, n, i)
|
addCaseBranchFacts(tracked.guards, n, i)
|
||||||
for i in 0 ..< branch.len:
|
for i in 0 ..< branch.len:
|
||||||
track(tracked, branch.sons[i])
|
track(tracked, branch.sons[i])
|
||||||
|
|
@ -631,11 +631,11 @@ proc trackCase(tracked: PEffects, n: PNode) =
|
||||||
for id, count in items(inter):
|
for id, count in items(inter):
|
||||||
if count >= toCover: tracked.init.add id
|
if count >= toCover: tracked.init.add id
|
||||||
# else we can't merge
|
# else we can't merge
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
|
|
||||||
proc trackIf(tracked: PEffects, n: PNode) =
|
proc trackIf(tracked: PEffects, n: PNode) =
|
||||||
track(tracked, n.sons[0].sons[0])
|
track(tracked, n.sons[0].sons[0])
|
||||||
let oldFacts = tracked.guards.len
|
let oldFacts = tracked.guards.s.len
|
||||||
addFact(tracked.guards, n.sons[0].sons[0])
|
addFact(tracked.guards, n.sons[0].sons[0])
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
|
|
||||||
|
|
@ -648,7 +648,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||||
|
|
||||||
for i in 1..<n.len:
|
for i in 1..<n.len:
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
for j in 0..i-1:
|
for j in 0..i-1:
|
||||||
addFactNeg(tracked.guards, n.sons[j].sons[0])
|
addFactNeg(tracked.guards, n.sons[j].sons[0])
|
||||||
if branch.len > 1:
|
if branch.len > 1:
|
||||||
|
|
@ -664,7 +664,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||||
for id, count in items(inter):
|
for id, count in items(inter):
|
||||||
if count >= toCover: tracked.init.add id
|
if count >= toCover: tracked.init.add id
|
||||||
# else we can't merge as it is not exhaustive
|
# else we can't merge as it is not exhaustive
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
|
|
||||||
proc trackBlock(tracked: PEffects, n: PNode) =
|
proc trackBlock(tracked: PEffects, n: PNode) =
|
||||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
|
|
@ -692,7 +692,7 @@ proc paramType(op: PType, i: int): PType =
|
||||||
proc cstringCheck(tracked: PEffects; n: PNode) =
|
proc cstringCheck(tracked: PEffects; n: PNode) =
|
||||||
if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
|
if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
|
||||||
a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
|
a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
|
||||||
message(n.info, warnUnsafeCode, renderTree(n))
|
message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
|
||||||
|
|
||||||
proc track(tracked: PEffects, n: PNode) =
|
proc track(tracked: PEffects, n: PNode) =
|
||||||
case n.kind
|
case n.kind
|
||||||
|
|
@ -734,7 +734,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
if notGcSafe(op) and not importedFromC(a):
|
if notGcSafe(op) and not importedFromC(a):
|
||||||
# and it's not a recursive call:
|
# and it's not a recursive call:
|
||||||
if not (a.kind == nkSym and a.sym == tracked.owner):
|
if not (a.kind == nkSym and a.sym == tracked.owner):
|
||||||
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
|
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
|
||||||
markGcUnsafe(tracked, a)
|
markGcUnsafe(tracked, a)
|
||||||
if tfNoSideEffect notin op.flags and not importedFromC(a):
|
if tfNoSideEffect notin op.flags and not importedFromC(a):
|
||||||
# and it's not a recursive call:
|
# and it's not a recursive call:
|
||||||
|
|
@ -752,7 +752,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
# var s: seq[notnil]; newSeq(s, 0) is a special case!
|
# var s: seq[notnil]; newSeq(s, 0) is a special case!
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
message(arg.info, warnProveInit, $arg)
|
message(tracked.config, arg.info, warnProveInit, $arg)
|
||||||
for i in 0 ..< safeLen(n):
|
for i in 0 ..< safeLen(n):
|
||||||
track(tracked, n.sons[i])
|
track(tracked, n.sons[i])
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
|
|
@ -760,7 +760,8 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
for i in 0 ..< len(n): track(tracked, n.sons[i])
|
for i in 0 ..< len(n): track(tracked, n.sons[i])
|
||||||
of nkCheckedFieldExpr:
|
of nkCheckedFieldExpr:
|
||||||
track(tracked, n.sons[0])
|
track(tracked, n.sons[0])
|
||||||
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
|
if warnProveField in tracked.config.notes:
|
||||||
|
checkFieldAccess(tracked.guards, n, tracked.config)
|
||||||
of nkTryStmt: trackTryStmt(tracked, n)
|
of nkTryStmt: trackTryStmt(tracked, n)
|
||||||
of nkPragma: trackPragmaStmt(tracked, n)
|
of nkPragma: trackPragmaStmt(tracked, n)
|
||||||
of nkAsgn, nkFastAsgn:
|
of nkAsgn, nkFastAsgn:
|
||||||
|
|
@ -797,11 +798,11 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
else:
|
else:
|
||||||
# loop may never execute:
|
# loop may never execute:
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
let oldFacts = tracked.guards.len
|
let oldFacts = tracked.guards.s.len
|
||||||
addFact(tracked.guards, n.sons[0])
|
addFact(tracked.guards, n.sons[0])
|
||||||
track(tracked, n.sons[1])
|
track(tracked, n.sons[1])
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
of nkForStmt, nkParForStmt:
|
of nkForStmt, nkParForStmt:
|
||||||
# we are very conservative here and assume the loop is never executed:
|
# we are very conservative here and assume the loop is never executed:
|
||||||
let oldState = tracked.init.len
|
let oldState = tracked.init.len
|
||||||
|
|
@ -810,13 +811,13 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
setLen(tracked.init, oldState)
|
setLen(tracked.init, oldState)
|
||||||
of nkObjConstr:
|
of nkObjConstr:
|
||||||
when false: track(tracked, n.sons[0])
|
when false: track(tracked, n.sons[0])
|
||||||
let oldFacts = tracked.guards.len
|
let oldFacts = tracked.guards.s.len
|
||||||
for i in 1 ..< len(n):
|
for i in 1 ..< len(n):
|
||||||
let x = n.sons[i]
|
let x = n.sons[i]
|
||||||
track(tracked, x)
|
track(tracked, x)
|
||||||
if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
|
if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
|
||||||
addDiscriminantFact(tracked.guards, x)
|
addDiscriminantFact(tracked.guards, x)
|
||||||
setLen(tracked.guards, oldFacts)
|
setLen(tracked.guards.s, oldFacts)
|
||||||
of nkPragmaBlock:
|
of nkPragmaBlock:
|
||||||
let pragmaList = n.sons[0]
|
let pragmaList = n.sons[0]
|
||||||
let oldLocked = tracked.locked.len
|
let oldLocked = tracked.locked.len
|
||||||
|
|
@ -843,31 +844,31 @@ proc track(tracked: PEffects, n: PNode) =
|
||||||
else:
|
else:
|
||||||
for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
|
for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
|
||||||
|
|
||||||
proc subtypeRelation(spec, real: PNode): bool =
|
proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
|
||||||
result = safeInheritanceDiff(real.excType, spec.typ) <= 0
|
result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0
|
||||||
|
|
||||||
proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool;
|
proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool;
|
||||||
effectPredicate: proc (a, b: PNode): bool {.nimcall.}) =
|
effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) =
|
||||||
# check that any real exception is listed in 'spec'; mark those as used;
|
# check that any real exception is listed in 'spec'; mark those as used;
|
||||||
# report any unused exception
|
# report any unused exception
|
||||||
var used = initIntSet()
|
var used = initIntSet()
|
||||||
for r in items(real):
|
for r in items(real):
|
||||||
block search:
|
block search:
|
||||||
for s in 0 ..< spec.len:
|
for s in 0 ..< spec.len:
|
||||||
if effectPredicate(spec[s], r):
|
if effectPredicate(g, spec[s], r):
|
||||||
used.incl(s)
|
used.incl(s)
|
||||||
break search
|
break search
|
||||||
# XXX call graph analysis would be nice here!
|
# XXX call graph analysis would be nice here!
|
||||||
pushInfoContext(spec.info)
|
pushInfoContext(spec.info)
|
||||||
localError(r.info, errGenerated, msg & typeToString(r.typ))
|
localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
# hint about unnecessarily listed exception types:
|
# hint about unnecessarily listed exception types:
|
||||||
if hints:
|
if hints:
|
||||||
for s in 0 ..< spec.len:
|
for s in 0 ..< spec.len:
|
||||||
if not used.contains(s):
|
if not used.contains(s):
|
||||||
message(spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
|
message(g.config, spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
|
||||||
|
|
||||||
proc checkMethodEffects*(disp, branch: PSym) =
|
proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
|
||||||
## checks for consistent effects for multi methods.
|
## checks for consistent effects for multi methods.
|
||||||
let actual = branch.typ.n.sons[0]
|
let actual = branch.typ.n.sons[0]
|
||||||
if actual.len != effectListLen: return
|
if actual.len != effectListLen: return
|
||||||
|
|
@ -875,42 +876,42 @@ proc checkMethodEffects*(disp, branch: PSym) =
|
||||||
let p = disp.ast.sons[pragmasPos]
|
let p = disp.ast.sons[pragmasPos]
|
||||||
let raisesSpec = effectSpec(p, wRaises)
|
let raisesSpec = effectSpec(p, wRaises)
|
||||||
if not isNil(raisesSpec):
|
if not isNil(raisesSpec):
|
||||||
checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects],
|
checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects],
|
||||||
"can raise an unlisted exception: ", hints=off, subtypeRelation)
|
"can raise an unlisted exception: ", hints=off, subtypeRelation)
|
||||||
let tagsSpec = effectSpec(p, wTags)
|
let tagsSpec = effectSpec(p, wTags)
|
||||||
if not isNil(tagsSpec):
|
if not isNil(tagsSpec):
|
||||||
checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
|
checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects],
|
||||||
"can have an unlisted effect: ", hints=off, subtypeRelation)
|
"can have an unlisted effect: ", hints=off, subtypeRelation)
|
||||||
if sfThread in disp.flags and notGcSafe(branch.typ):
|
if sfThread in disp.flags and notGcSafe(branch.typ):
|
||||||
localError(branch.info, "base method is GC-safe, but '$1' is not" %
|
localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" %
|
||||||
branch.name.s)
|
branch.name.s)
|
||||||
if branch.typ.lockLevel > disp.typ.lockLevel:
|
if branch.typ.lockLevel > disp.typ.lockLevel:
|
||||||
when true:
|
when true:
|
||||||
message(branch.info, warnLockLevel,
|
message(g.config, branch.info, warnLockLevel,
|
||||||
"base method has lock level $1, but dispatcher has $2" %
|
"base method has lock level $1, but dispatcher has $2" %
|
||||||
[$branch.typ.lockLevel, $disp.typ.lockLevel])
|
[$branch.typ.lockLevel, $disp.typ.lockLevel])
|
||||||
else:
|
else:
|
||||||
# XXX make this an error after bigbreak has been released:
|
# XXX make this an error after bigbreak has been released:
|
||||||
localError(branch.info,
|
localError(g.config, branch.info,
|
||||||
"base method has lock level $1, but dispatcher has $2" %
|
"base method has lock level $1, but dispatcher has $2" %
|
||||||
[$branch.typ.lockLevel, $disp.typ.lockLevel])
|
[$branch.typ.lockLevel, $disp.typ.lockLevel])
|
||||||
|
|
||||||
proc setEffectsForProcType*(t: PType, n: PNode) =
|
proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) =
|
||||||
var effects = t.n.sons[0]
|
var effects = t.n.sons[0]
|
||||||
internalAssert t.kind == tyProc and effects.kind == nkEffectList
|
if t.kind != tyProc or effects.kind != nkEffectList: return
|
||||||
|
|
||||||
let
|
let
|
||||||
raisesSpec = effectSpec(n, wRaises)
|
raisesSpec = effectSpec(n, wRaises)
|
||||||
tagsSpec = effectSpec(n, wTags)
|
tagsSpec = effectSpec(n, wTags)
|
||||||
if not isNil(raisesSpec) or not isNil(tagsSpec):
|
if not isNil(raisesSpec) or not isNil(tagsSpec):
|
||||||
internalAssert effects.len == 0
|
internalAssert g.config, effects.len == 0
|
||||||
newSeq(effects.sons, effectListLen)
|
newSeq(effects.sons, effectListLen)
|
||||||
if not isNil(raisesSpec):
|
if not isNil(raisesSpec):
|
||||||
effects.sons[exceptionEffects] = raisesSpec
|
effects.sons[exceptionEffects] = raisesSpec
|
||||||
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) =
|
proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) =
|
||||||
newSeq(effects.sons, effectListLen)
|
newSeq(effects.sons, effectListLen)
|
||||||
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
|
effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
|
||||||
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
|
effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
|
||||||
|
|
@ -921,36 +922,39 @@ proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
|
||||||
t.tags = effects.sons[tagEffects]
|
t.tags = effects.sons[tagEffects]
|
||||||
t.owner = s
|
t.owner = s
|
||||||
t.init = @[]
|
t.init = @[]
|
||||||
t.guards = @[]
|
t.guards.s = @[]
|
||||||
|
t.guards.o = initOperators(g)
|
||||||
t.locked = @[]
|
t.locked = @[]
|
||||||
|
t.graph = g
|
||||||
|
t.config = g.config
|
||||||
|
|
||||||
proc trackProc*(s: PSym, body: PNode) =
|
proc trackProc*(g: ModuleGraph; s: PSym, body: PNode) =
|
||||||
var effects = s.typ.n.sons[0]
|
var effects = s.typ.n.sons[0]
|
||||||
internalAssert effects.kind == nkEffectList
|
if effects.kind != nkEffectList: return
|
||||||
# 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
|
||||||
|
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
initEffects(effects, s, t)
|
initEffects(g, effects, s, t)
|
||||||
track(t, body)
|
track(t, body)
|
||||||
if not isEmptyType(s.typ.sons[0]) and
|
if not isEmptyType(s.typ.sons[0]) and
|
||||||
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
|
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
|
||||||
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
s.kind in {skProc, skFunc, skConverter, skMethod}:
|
||||||
var res = s.ast.sons[resultPos].sym # get result symbol
|
var res = s.ast.sons[resultPos].sym # get result symbol
|
||||||
if res.id notin t.init:
|
if res.id notin t.init:
|
||||||
message(body.info, warnProveInit, "result")
|
message(g.config, body.info, warnProveInit, "result")
|
||||||
let p = s.ast.sons[pragmasPos]
|
let p = s.ast.sons[pragmasPos]
|
||||||
let raisesSpec = effectSpec(p, wRaises)
|
let raisesSpec = effectSpec(p, wRaises)
|
||||||
if not isNil(raisesSpec):
|
if not isNil(raisesSpec):
|
||||||
checkRaisesSpec(raisesSpec, t.exc, "can raise an unlisted exception: ",
|
checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ",
|
||||||
hints=on, subtypeRelation)
|
hints=on, subtypeRelation)
|
||||||
# after the check, use the formal spec:
|
# after the check, use the formal spec:
|
||||||
effects.sons[exceptionEffects] = raisesSpec
|
effects.sons[exceptionEffects] = raisesSpec
|
||||||
|
|
||||||
let tagsSpec = effectSpec(p, wTags)
|
let tagsSpec = effectSpec(p, wTags)
|
||||||
if not isNil(tagsSpec):
|
if not isNil(tagsSpec):
|
||||||
checkRaisesSpec(tagsSpec, t.tags, "can have an unlisted effect: ",
|
checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ",
|
||||||
hints=off, subtypeRelation)
|
hints=off, subtypeRelation)
|
||||||
# after the check, use the formal spec:
|
# after the check, use the formal spec:
|
||||||
effects.sons[tagEffects] = tagsSpec
|
effects.sons[tagEffects] = tagsSpec
|
||||||
|
|
@ -958,15 +962,15 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
if sfThread in s.flags and t.gcUnsafe:
|
if sfThread in s.flags and t.gcUnsafe:
|
||||||
if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
|
if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
|
||||||
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
||||||
listGcUnsafety(s, onlyWarning=false)
|
listGcUnsafety(s, onlyWarning=false, g.config)
|
||||||
else:
|
else:
|
||||||
listGcUnsafety(s, onlyWarning=true)
|
listGcUnsafety(s, onlyWarning=true, g.config)
|
||||||
#localError(s.info, warnGcUnsafe2, s.name.s)
|
#localError(s.info, warnGcUnsafe2, s.name.s)
|
||||||
if sfNoSideEffect in s.flags and t.hasSideEffect:
|
if sfNoSideEffect in s.flags and t.hasSideEffect:
|
||||||
when false:
|
when false:
|
||||||
listGcUnsafety(s, onlyWarning=false)
|
listGcUnsafety(s, onlyWarning=false, g.config)
|
||||||
else:
|
else:
|
||||||
localError(s.info, errXhasSideEffects, s.name.s)
|
localError(g.config, s.info, "'$1' has side effects" % s.name.s)
|
||||||
if not t.gcUnsafe:
|
if not t.gcUnsafe:
|
||||||
s.typ.flags.incl tfGcSafe
|
s.typ.flags.incl tfGcSafe
|
||||||
if not t.hasSideEffect and sfSideEffect notin s.flags:
|
if not t.hasSideEffect and sfSideEffect notin s.flags:
|
||||||
|
|
@ -975,7 +979,7 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
s.typ.lockLevel = t.maxLockLevel
|
s.typ.lockLevel = t.maxLockLevel
|
||||||
elif t.maxLockLevel > s.typ.lockLevel:
|
elif t.maxLockLevel > s.typ.lockLevel:
|
||||||
#localError(s.info,
|
#localError(s.info,
|
||||||
message(s.info, warnLockLevel,
|
message(g.config, s.info, warnLockLevel,
|
||||||
"declared lock level is $1, but real lock level is $2" %
|
"declared lock level is $1, but real lock level is $2" %
|
||||||
[$s.typ.lockLevel, $t.maxLockLevel])
|
[$s.typ.lockLevel, $t.maxLockLevel])
|
||||||
when defined(useDfa):
|
when defined(useDfa):
|
||||||
|
|
@ -983,12 +987,12 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||||
dataflowAnalysis(s, body)
|
dataflowAnalysis(s, body)
|
||||||
when false: trackWrites(s, body)
|
when false: trackWrites(s, body)
|
||||||
|
|
||||||
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
proc trackTopLevelStmt*(g: ModuleGraph; module: PSym; n: PNode) =
|
||||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||||
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||||
return
|
return
|
||||||
var effects = newNode(nkEffectList, n.info)
|
var effects = newNode(nkEffectList, n.info)
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
initEffects(effects, module, t)
|
initEffects(g, effects, module, t)
|
||||||
t.isToplevel = true
|
t.isToplevel = true
|
||||||
track(t, n)
|
track(t, n)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue