sempass2 compiles again

This commit is contained in:
Andreas Rumpf
2018-05-11 14:18:39 +02:00
parent 7eb39d9d26
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",
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
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",
errImplOfXNotAllowed: "implementation of '$1' is not allowed",
errImplOfXexpected: ,

View File

@@ -402,8 +402,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
type
TModel* = object
s: seq[PNode] # the "knowledge base"
o: Operators
s*: seq[PNode] # the "knowledge base"
o*: Operators
proc addFact*(m: var TModel, nn: PNode) =
let n = usefulFact(nn, m.o)

View File

@@ -9,7 +9,8 @@
import
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):
import dfa
@@ -53,6 +54,7 @@ type
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
maxLockLevel, currLockLevel: TLockLevel
config: ConfigRef
graph: ModuleGraph
PEffects = var TEffects
proc `<`(a, b: TLockLevel): bool {.borrow.}
@@ -257,32 +259,29 @@ proc addToIntersection(inter: var TIntersection, s: int) =
proc throws(tracked, n: PNode) =
if n.typ == nil or n.typ.kind != tyError: tracked.add n
proc getEbase(g: ModuleGraph): PType =
result = g.sysTypeFromName"Exception"
proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
result = g.sysTypeFromName(info, "Exception")
proc excType(n: PNode): PType =
proc excType(g: ModuleGraph; n: PNode): PType =
# 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)
proc createRaise(n: PNode): PNode =
proc createRaise(g: ModuleGraph; n: PNode): PNode =
result = newNode(nkType)
result.typ = getEbase()
result.typ = getEbase(g, 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)
if getCompilerProc("RootEffect") != nil:
result.typ = sysTypeFromName"RootEffect"
else:
result.typ = sysTypeFromName"TEffect"
result.typ = g.sysTypeFromName(n.info, "RootEffect")
if not n.isNil: result.info = n.info
proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
assert e.kind != nkRaiseStmt
var aa = a.exc
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
elif aa[i].info == e.info: return
throws(a.exc, e)
@@ -297,19 +296,19 @@ proc addTag(a: PEffects, e: PNode, useLineInfo=true) =
proc mergeEffects(a: PEffects, b, comesFrom: PNode) =
if b.isNil:
addEffect(a, createRaise(comesFrom))
addEffect(a, createRaise(a.graph, comesFrom))
else:
for effect in items(b): addEffect(a, effect, useLineInfo=comesFrom != nil)
proc mergeTags(a: PEffects, b, comesFrom: PNode) =
if b.isNil:
addTag(a, createTag(comesFrom))
addTag(a, createTag(a.graph, comesFrom))
else:
for effect in items(b): addTag(a, effect, useLineInfo=comesFrom != nil)
proc listEffects(a: PEffects) =
for e in items(a.exc): message(e.info, hintUser, typeToString(e.typ))
for e in items(a.tags): 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(a.config, e.info, hintUser, typeToString(e.typ))
#if a.maxLockLevel != 0:
# message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
@@ -319,7 +318,7 @@ proc catches(tracked: PEffects, e: PType) =
var i = tracked.bottom
while i < L:
# 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]
dec L
else:
@@ -488,7 +487,7 @@ proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
if lockLevel >= tracked.currLockLevel:
# if in lock section:
if tracked.currLockLevel > 0.TLockLevel:
localError n.info, errGenerated,
localError tracked.config, n.info, errGenerated,
"expected lock level < " & $tracked.currLockLevel &
" but got lock level " & $lockLevel
tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
@@ -502,22 +501,22 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
mergeTags(tracked, tagSpec, n)
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)
if tfNoSideEffect notin s.typ.flags:
markSideEffect(tracked, s)
mergeLockLevels(tracked, n, s.getLockLevel)
proc procVarcheck(n: PNode) =
proc procVarcheck(n: PNode; conf: ConfigRef) =
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:
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) =
let n = n.skipConv
if paramType.isNil or paramType.kind != tyTypeDesc:
procVarcheck skipConvAndClosure(n)
procVarcheck skipConvAndClosure(n), tracked.config
#elif n.kind in nkSymChoices:
# echo "came here"
let paramType = paramType.skipTypesOrNil(abstractInst)
@@ -533,15 +532,16 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
return
case impliesNotNil(tracked.guards, n)
of impUnknown:
message(n.info, errGenerated,
message(tracked.config, n.info, errGenerated,
"cannot prove '$1' is not nil" % n.renderTree)
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
proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
addEffect(tracked, createRaise(n))
addTag(tracked, createTag(n))
addEffect(tracked, createRaise(tracked.graph, n))
addTag(tracked, createTag(tracked.graph, n))
let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
else: op.lockLevel
#if lockLevel == UnknownLockLevel:
@@ -556,7 +556,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
let a = skipConvAndClosure(n)
let op = a.typ
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]
let s = n.skipConv
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)
# assume GcUnsafe unless in its type; 'forward' does not matter:
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)
elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner):
markSideEffect(tracked, a)
@@ -579,7 +579,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
if notGcSafe(op):
if warnGcUnsafe in gNotes: warnAboutGcUnsafe(n)
if warnGcUnsafe in tracked.config.notes: warnAboutGcUnsafe(n, tracked.config)
markGcUnsafe(tracked, a)
elif tfNoSideEffect notin op.flags:
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
# example that triggers it.
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)
proc breaksBlock(n: PNode): bool =
@@ -607,18 +607,18 @@ proc breaksBlock(n: PNode): bool =
proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0])
let oldState = tracked.init.len
let oldFacts = tracked.guards.len
let oldFacts = tracked.guards.s.len
let stringCase = skipTypes(n.sons[0].typ,
abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString}
let interesting = not stringCase and interestingCaseExpr(n.sons[0]) and
warnProveField in gNotes
warnProveField in tracked.config.notes
var inter: TIntersection = @[]
var toCover = 0
for i in 1..<n.len:
let branch = n.sons[i]
setLen(tracked.init, oldState)
if interesting:
setLen(tracked.guards, oldFacts)
setLen(tracked.guards.s, oldFacts)
addCaseBranchFacts(tracked.guards, n, i)
for i in 0 ..< branch.len:
track(tracked, branch.sons[i])
@@ -631,11 +631,11 @@ proc trackCase(tracked: PEffects, n: PNode) =
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# else we can't merge
setLen(tracked.guards, oldFacts)
setLen(tracked.guards.s, oldFacts)
proc trackIf(tracked: PEffects, n: PNode) =
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])
let oldState = tracked.init.len
@@ -648,7 +648,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
for i in 1..<n.len:
let branch = n.sons[i]
setLen(tracked.guards, oldFacts)
setLen(tracked.guards.s, oldFacts)
for j in 0..i-1:
addFactNeg(tracked.guards, n.sons[j].sons[0])
if branch.len > 1:
@@ -664,7 +664,7 @@ proc trackIf(tracked: PEffects, n: PNode) =
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# 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) =
if n.kind in {nkStmtList, nkStmtListExpr}:
@@ -692,7 +692,7 @@ proc paramType(op: PType, i: int): PType =
proc cstringCheck(tracked: PEffects; n: PNode) =
if n.sons[0].typ.kind == tyCString and (let a = skipConv(n[1]);
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) =
case n.kind
@@ -734,7 +734,7 @@ proc track(tracked: PEffects, n: PNode) =
if notGcSafe(op) and not importedFromC(a):
# and it's not a recursive call:
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)
if tfNoSideEffect notin op.flags and not importedFromC(a):
# 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!
discard
else:
message(arg.info, warnProveInit, $arg)
message(tracked.config, arg.info, warnProveInit, $arg)
for i in 0 ..< safeLen(n):
track(tracked, n.sons[i])
of nkDotExpr:
@@ -760,7 +760,8 @@ proc track(tracked: PEffects, n: PNode) =
for i in 0 ..< len(n): track(tracked, n.sons[i])
of nkCheckedFieldExpr:
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 nkPragma: trackPragmaStmt(tracked, n)
of nkAsgn, nkFastAsgn:
@@ -797,11 +798,11 @@ proc track(tracked: PEffects, n: PNode) =
else:
# loop may never execute:
let oldState = tracked.init.len
let oldFacts = tracked.guards.len
let oldFacts = tracked.guards.s.len
addFact(tracked.guards, n.sons[0])
track(tracked, n.sons[1])
setLen(tracked.init, oldState)
setLen(tracked.guards, oldFacts)
setLen(tracked.guards.s, oldFacts)
of nkForStmt, nkParForStmt:
# we are very conservative here and assume the loop is never executed:
let oldState = tracked.init.len
@@ -810,13 +811,13 @@ proc track(tracked: PEffects, n: PNode) =
setLen(tracked.init, oldState)
of nkObjConstr:
when false: track(tracked, n.sons[0])
let oldFacts = tracked.guards.len
let oldFacts = tracked.guards.s.len
for i in 1 ..< len(n):
let x = n.sons[i]
track(tracked, x)
if x.sons[0].kind == nkSym and sfDiscriminant in x.sons[0].sym.flags:
addDiscriminantFact(tracked.guards, x)
setLen(tracked.guards, oldFacts)
setLen(tracked.guards.s, oldFacts)
of nkPragmaBlock:
let pragmaList = n.sons[0]
let oldLocked = tracked.locked.len
@@ -843,31 +844,31 @@ proc track(tracked: PEffects, n: PNode) =
else:
for i in 0 ..< safeLen(n): track(tracked, n.sons[i])
proc subtypeRelation(spec, real: PNode): bool =
result = safeInheritanceDiff(real.excType, spec.typ) <= 0
proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
result = safeInheritanceDiff(g.excType(real), spec.typ) <= 0
proc checkRaisesSpec(spec, real: PNode, msg: string, hints: bool;
effectPredicate: proc (a, b: PNode): bool {.nimcall.}) =
proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool;
effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) =
# check that any real exception is listed in 'spec'; mark those as used;
# report any unused exception
var used = initIntSet()
for r in items(real):
block search:
for s in 0 ..< spec.len:
if effectPredicate(spec[s], r):
if effectPredicate(g, spec[s], r):
used.incl(s)
break search
# XXX call graph analysis would be nice here!
pushInfoContext(spec.info)
localError(r.info, errGenerated, msg & typeToString(r.typ))
localError(g.config, r.info, errGenerated, msg & typeToString(r.typ))
popInfoContext()
# hint about unnecessarily listed exception types:
if hints:
for s in 0 ..< spec.len:
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.
let actual = branch.typ.n.sons[0]
if actual.len != effectListLen: return
@@ -875,42 +876,42 @@ proc checkMethodEffects*(disp, branch: PSym) =
let p = disp.ast.sons[pragmasPos]
let raisesSpec = effectSpec(p, wRaises)
if not isNil(raisesSpec):
checkRaisesSpec(raisesSpec, actual.sons[exceptionEffects],
checkRaisesSpec(g, raisesSpec, actual.sons[exceptionEffects],
"can raise an unlisted exception: ", hints=off, subtypeRelation)
let tagsSpec = effectSpec(p, wTags)
if not isNil(tagsSpec):
checkRaisesSpec(tagsSpec, actual.sons[tagEffects],
checkRaisesSpec(g, tagsSpec, actual.sons[tagEffects],
"can have an unlisted effect: ", hints=off, subtypeRelation)
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)
if branch.typ.lockLevel > disp.typ.lockLevel:
when true:
message(branch.info, warnLockLevel,
message(g.config, branch.info, warnLockLevel,
"base method has lock level $1, but dispatcher has $2" %
[$branch.typ.lockLevel, $disp.typ.lockLevel])
else:
# 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" %
[$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]
internalAssert t.kind == tyProc and effects.kind == nkEffectList
if t.kind != tyProc or effects.kind != nkEffectList: return
let
raisesSpec = effectSpec(n, wRaises)
tagsSpec = effectSpec(n, wTags)
if not isNil(raisesSpec) or not isNil(tagsSpec):
internalAssert effects.len == 0
internalAssert g.config, effects.len == 0
newSeq(effects.sons, effectListLen)
if not isNil(raisesSpec):
effects.sons[exceptionEffects] = raisesSpec
if not isNil(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)
effects.sons[exceptionEffects] = 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.owner = s
t.init = @[]
t.guards = @[]
t.guards.s = @[]
t.guards.o = initOperators(g)
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]
internalAssert effects.kind == nkEffectList
if effects.kind != nkEffectList: return
# effects already computed?
if sfForward in s.flags: return
if effects.len == effectListLen: return
var t: TEffects
initEffects(effects, s, t)
initEffects(g, effects, s, t)
track(t, body)
if not isEmptyType(s.typ.sons[0]) and
{tfNeedsInit, tfNotNil} * s.typ.sons[0].flags != {} and
s.kind in {skProc, skFunc, skConverter, skMethod}:
var res = s.ast.sons[resultPos].sym # get result symbol
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 raisesSpec = effectSpec(p, wRaises)
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)
# after the check, use the formal spec:
effects.sons[exceptionEffects] = raisesSpec
let tagsSpec = effectSpec(p, wTags)
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)
# after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec
@@ -958,15 +962,15 @@ proc trackProc*(s: PSym, body: PNode) =
if sfThread in s.flags and t.gcUnsafe:
if optThreads in gGlobalOptions and optThreadAnalysis in gGlobalOptions:
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
listGcUnsafety(s, onlyWarning=false)
listGcUnsafety(s, onlyWarning=false, g.config)
else:
listGcUnsafety(s, onlyWarning=true)
listGcUnsafety(s, onlyWarning=true, g.config)
#localError(s.info, warnGcUnsafe2, s.name.s)
if sfNoSideEffect in s.flags and t.hasSideEffect:
when false:
listGcUnsafety(s, onlyWarning=false)
listGcUnsafety(s, onlyWarning=false, g.config)
else:
localError(s.info, errXhasSideEffects, s.name.s)
localError(g.config, s.info, "'$1' has side effects" % s.name.s)
if not t.gcUnsafe:
s.typ.flags.incl tfGcSafe
if not t.hasSideEffect and sfSideEffect notin s.flags:
@@ -975,7 +979,7 @@ proc trackProc*(s: PSym, body: PNode) =
s.typ.lockLevel = t.maxLockLevel
elif t.maxLockLevel > s.typ.lockLevel:
#localError(s.info,
message(s.info, warnLockLevel,
message(g.config, s.info, warnLockLevel,
"declared lock level is $1, but real lock level is $2" %
[$s.typ.lockLevel, $t.maxLockLevel])
when defined(useDfa):
@@ -983,12 +987,12 @@ proc trackProc*(s: PSym, body: PNode) =
dataflowAnalysis(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,
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
return
var effects = newNode(nkEffectList, n.info)
var t: TEffects
initEffects(effects, module, t)
initEffects(g, effects, module, t)
t.isToplevel = true
track(t, n)