next steps for exception tracking

This commit is contained in:
Araq 2012-11-03 15:57:12 +01:00
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. ## This module implements code generation for multi methods.
import 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 = proc genConv(n: PNode, d: PType, downcast: bool): PNode =
var dest = skipTypes(d, abstractPtrs) var dest = skipTypes(d, abstractPtrs)
@ -81,10 +82,12 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
proc methodDef*(s: PSym, fromCache: bool) = proc methodDef*(s: PSym, fromCache: bool) =
var L = len(gMethods) var L = len(gMethods)
for i in countup(0, L - 1): for i in countup(0, L - 1):
if sameMethodBucket(gMethods[i][0], s): let disp = gMethods[i][0]
if sameMethodBucket(disp, s):
add(gMethods[i], s) add(gMethods[i], s)
attachDispatcher(s, lastSon(gMethods[i][0].ast)) attachDispatcher(s, lastSon(disp.ast))
when useEffectSystem: checkMethodEffects(disp, s)
return return
add(gMethods, @[s]) add(gMethods, @[s])
# create a new dispatcher: # create a new dispatcher:

View file

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

View file

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

View file

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

View file

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

View file

@ -1189,3 +1189,26 @@ proc baseOfDistinct*(t: PType): PType =
if it.kind == tyDistinct: if it.kind == tyDistinct:
internalAssert parent != nil internalAssert parent != nil
parent.sons[0] = it.sons[0] 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