next steps for exception tracking

This commit is contained in:
Araq
2012-11-03 15:57:12 +01:00
parent 224f42bbd7
commit 9f38ff0c65
7 changed files with 73 additions and 34 deletions

View File

@@ -10,7 +10,8 @@
## This module implements code generation for multi methods.
import
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
sempass2
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var dest = skipTypes(d, abstractPtrs)
@@ -81,10 +82,12 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
proc methodDef*(s: PSym, fromCache: bool) =
var L = len(gMethods)
for i in countup(0, L - 1):
if sameMethodBucket(gMethods[i][0], s):
for i in countup(0, L - 1):
let disp = gMethods[i][0]
if sameMethodBucket(disp, s):
add(gMethods[i], s)
attachDispatcher(s, lastSon(gMethods[i][0].ast))
attachDispatcher(s, lastSon(disp.ast))
when useEffectSystem: checkMethodEffects(disp, s)
return
add(gMethods, @[s])
# create a new dispatcher:

View File

@@ -12,7 +12,7 @@ import
const
hasTinyCBackend* = defined(tinyc)
useEffectSystem* = false
useEffectSystem* = true
type # please make sure we have under 32 options
# (improves code efficiency a lot!)

View File

@@ -15,7 +15,7 @@ import
magicsys, parser, nversion, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns
evaltempl, patterns, parampatterns, sempass2
proc semPass*(): TPass
# implementation

View File

@@ -25,7 +25,7 @@ import
# io, time (time dependent), gc (performs GC'ed allocation), exceptions,
# side effect (accesses global), store (stores into *type*),
# store_unkown (performs some store) --> store(any)|store(x)
# load (loads from *type*), recursive (recursive call),
# load (loads from *type*), recursive (recursive call), unsafe,
# endless (has endless loops), --> user effects are defined over *patterns*
# --> a TR macro can annotate the proc with user defined annotations
# --> the effect system can access these
@@ -84,20 +84,16 @@ type
PEffects = var TEffects
proc throws(tracked: PEffects, n: PNode) =
# since a 'raise' statement occurs rarely and we need distinct reasons;
# we simply do not merge anything here, this would be problematic for the
# stack of exceptions anyway:
tracked.exc.add n
proc excType(n: PNode): PType =
assert n.kind == nkRaiseStmt
assert n.kind != nkRaiseStmt
# reraise is like raising E_Base:
let t = if n.sons[0].kind == nkEmpty: sysTypeFromName"E_Base"
else: n.sons[0].typ
let t = if n.kind == nkEmpty: sysTypeFromName"E_Base" else: n.typ
result = skipTypes(t, skipPtrs)
proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
assert e.kind == nkRaiseStmt
assert e.kind != nkRaiseStmt
var aa = a.exc
for i in a.bottom .. <aa.len:
if sameType(aa[i].excType, e.excType):
@@ -111,7 +107,7 @@ proc mergeEffects(a: PEffects, b: PNode, useLineInfo) =
proc listEffects(a: PEffects) =
var aa = a.exc
for e in items(aa):
Message(e.info, hintUser, renderTree(e))
Message(e.info, hintUser, typeToString(e.typ))
proc catches(tracked: PEffects, e: PType) =
let e = skipTypes(e, skipPtrs)
@@ -172,13 +168,12 @@ proc raisesSpec(n: PNode): PNode =
result.add(it.sons[1])
return
proc createRaise(n: PNode, t: PType): PNode =
result = newNodeI(nkRaiseStmt, n.info)
result.add(newNodeIT(nkType, n.info, t))
proc createRaise(n: PNode): PNode =
result = newNodeIT(nkType, n.info, sysTypeFromName"E_Base")
proc track(tracked: PEffects, n: PNode) =
case n.kind
of nkRaiseStmt: throws(tracked, n)
of nkRaiseStmt: throws(tracked, n.sons[0])
of nkCallKinds:
# p's effects are ours too:
let op = n.sons[0].typ
@@ -191,9 +186,9 @@ proc track(tracked: PEffects, n: PNode) =
if not isNil(spec):
mergeEffects(tracked, spec, useLineInfo=false)
else:
addEffect(tracked, createRaise(n, sysTypeFromName"E_Base"))
addEffect(tracked, createRaise(n))
elif isIndirectCall(n.sons[0]):
addEffect(tracked, createRaise(n, sysTypeFromName"E_Base"))
addEffect(tracked, createRaise(n))
else:
effectList = effectList.sons[exceptionEffects]
mergeEffects(tracked, effectList, useLineInfo=true)
@@ -209,7 +204,7 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, n.sons[i])
# XXX
# - make use of 'raises' in proc types compatibility
# - doc2 should report effects
# - check for 'raises' consistency for multi-methods
proc checkRaisesSpec(spec, real: PNode) =
@@ -224,22 +219,37 @@ proc checkRaisesSpec(spec, real: PNode) =
break search
# XXX call graph analysis would be nice here!
localError(r.info, errGenerated, "can raise an unlisted exception: " &
typeToString(r.sons[0].typ))
typeToString(r.typ))
# hint about unnecessarily listed exception types:
for s in 0 .. <spec.len:
if not used.contains(s):
Message(spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
proc compatibleEffects*(formal, actual: PType): bool =
# for proc type compatibility checking:
assert formal.kind == tyProc and actual.kind == tyProc
InternalAssert formal.n.sons[0].kind == nkEffectList
InternalAssert actual.n.sons[0].kind == nkEffectList
var effectList = formal.n.sons[0]
if effectList.len == 0:
# 'formal' has no restrictions :-)
result = true
proc checkMethodEffects*(disp, branch: PSym) =
## checks for consistent effects for multi methods.
let spec = raisesSpec(disp.ast.sons[pragmasPos])
if not isNil(spec):
let actual = branch.typ.n.sons[0]
if actual.len != effectListLen: return
let real = actual.sons[exceptionEffects]
for r in items(real):
block search:
for s in 0 .. <spec.len:
if inheritanceDiff(r.excType, spec[s].typ) <= 0:
break search
localError(r.info, errGenerated, "can raise an unlisted exception: " &
typeToString(r.typ))
proc setEffectsForProcType*(t: PType, n: PNode) =
var effects = t.n.sons[0]
InternalAssert t.kind == tyProc and effects.kind == nkEffectList
let spec = raisesSpec(n)
if not isNil(spec):
InternalAssert effects.len == 0
newSeq(effects.sons, effectListLen)
effects.sons[exceptionEffects] = spec
proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0]

View File

@@ -893,9 +893,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if n.sons[1].kind == nkEmpty or n.sons[1].len == 0:
if result.callConv == ccDefault:
result.callConv = ccClosure
Message(n.info, warnImplicitClosure, renderTree(n))
#Message(n.info, warnImplicitClosure, renderTree(n))
else:
pragma(c, s, n.sons[1], procTypePragmas)
when useEffectSystem: SetEffectsForProcType(result, n.sons[1])
closeScope(c.tab)
of nkEnumTy: result = semEnum(c, n, prev)
of nkType: result = n.typ

View File

@@ -297,6 +297,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result = isConvertible
else:
return isNone
when useEffectSystem:
if not compatibleEffects(f, a): return isNone
else: nil
proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =

View File

@@ -1189,3 +1189,26 @@ proc baseOfDistinct*(t: PType): PType =
if it.kind == tyDistinct:
internalAssert parent != nil
parent.sons[0] = it.sons[0]
proc compatibleEffects*(formal, actual: PType): bool =
# for proc type compatibility checking:
assert formal.kind == tyProc and actual.kind == tyProc
InternalAssert formal.n.sons[0].kind == nkEffectList
InternalAssert actual.n.sons[0].kind == nkEffectList
var spec = formal.n.sons[0]
# if 'spec.sons[0].kind == nkArgList' it is no formal type really, but a
# computed effect and as such no spec:
# 'r.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler'
if spec.len != 0 and spec.sons[0].kind != nkArgList:
var real = actual.n.sons[0]
if real.len == 0:
# we don't know anything about 'real' ...
return false
for r in items(real):
block search:
for s in items(spec):
if inheritanceDiff(r.typ, s.typ) <= 0:
break search
return false
result = true