sempass2 compiles again

This commit is contained in:
Andreas Rumpf 2018-05-11 14:18:39 +02:00
commit 2e76a92fe1
3 changed files with 85 additions and 81 deletions

View file

@ -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: ,

View file

@ -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)

View file

@ -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)