exception tracking should work

This commit is contained in:
Araq 2012-11-04 18:09:15 +01:00
commit 6dd2c2d767
7 changed files with 105 additions and 18 deletions

View file

@ -14,7 +14,7 @@
import import
ast, strutils, strtabs, options, msgs, os, ropes, idents, ast, strutils, strtabs, options, msgs, os, ropes, idents,
wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite, wordrecg, syntaxes, renderer, lexer, rstast, rst, rstgen, times, highlite,
importer importer, sempass2
type type
TSections = array[TSymKind, PRope] TSections = array[TSymKind, PRope]
@ -245,12 +245,20 @@ proc traceDeps(d: PDoc, n: PNode) =
proc generateDoc*(d: PDoc, n: PNode) = proc generateDoc*(d: PDoc, n: PNode) =
case n.kind case n.kind
of nkCommentStmt: app(d.modDesc, genComment(d, n)) of nkCommentStmt: app(d.modDesc, genComment(d, n))
of nkProcDef: genItem(d, n, n.sons[namePos], skProc) of nkProcDef:
of nkMethodDef: genItem(d, n, n.sons[namePos], skMethod) when useEffectSystem: documentRaises(n)
of nkIteratorDef: genItem(d, n, n.sons[namePos], skIterator) genItem(d, n, n.sons[namePos], skProc)
of nkMethodDef:
when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skMethod)
of nkIteratorDef:
when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skIterator)
of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro) of nkMacroDef: genItem(d, n, n.sons[namePos], skMacro)
of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate) of nkTemplateDef: genItem(d, n, n.sons[namePos], skTemplate)
of nkConverterDef: genItem(d, n, n.sons[namePos], skConverter) of nkConverterDef:
when useEffectSystem: documentRaises(n)
genItem(d, n, n.sons[namePos], skConverter)
of nkTypeSection, nkVarSection, nkLetSection, nkConstSection: of nkTypeSection, nkVarSection, nkLetSection, nkConstSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkCommentStmt: if n.sons[i].kind != nkCommentStmt:

View file

@ -9,7 +9,7 @@
import import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
wordrecg wordrecg, strutils
# Second semantic checking pass over the AST. Necessary because the old # Second semantic checking pass over the AST. Necessary because the old
# way had some inherent problems. Performs: # way had some inherent problems. Performs:
@ -158,7 +158,7 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
# list the computed effects up to here: # list the computed effects up to here:
listEffects(tracked) listEffects(tracked)
proc raisesSpec(n: PNode): PNode = proc raisesSpec*(n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i] var it = n.sons[i]
if it.kind == nkExprColonExpr and whichPragma(it) == wRaises: if it.kind == nkExprColonExpr and whichPragma(it) == wRaises:
@ -168,26 +168,62 @@ proc raisesSpec(n: PNode): PNode =
result.add(it.sons[1]) result.add(it.sons[1])
return return
proc documentRaises*(n: PNode) =
if n.sons[namePos].kind != nkSym: return
var x = n.sons[pragmasPos]
let spec = raisesSpec(x)
if isNil(spec):
let s = n.sons[namePos].sym
let actual = s.typ.n.sons[0]
if actual.len != effectListLen: return
let real = actual.sons[exceptionEffects]
# warning: hack ahead:
var effects = newNodeI(nkBracket, n.info, real.len)
for i in 0 .. <real.len:
var t = typeToString(real[i].typ)
if t.startsWith("ref "): t = substr(t, 4)
effects.sons[i] = newIdentNode(getIdent(t), n.info)
var pair = newNode(nkExprColonExpr, n.info, @[
newIdentNode(getIdent"raises", n.info), effects])
if x.kind == nkEmpty:
x = newNodeI(nkPragma, n.info)
n.sons[pragmasPos] = x
x.add(pair)
proc createRaise(n: PNode): PNode = proc createRaise(n: PNode): PNode =
result = newNodeIT(nkType, n.info, sysTypeFromName"E_Base") result = newNodeIT(nkType, n.info, sysTypeFromName"E_Base")
proc track(tracked: PEffects, n: PNode) = proc track(tracked: PEffects, n: PNode) =
case n.kind case n.kind
of nkRaiseStmt: throws(tracked, n.sons[0]) of nkRaiseStmt:
n.sons[0].info = n.info
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 a = n.sons[0]
let op = a.typ
if op != nil and op.kind == tyProc: if op != nil and op.kind == tyProc:
InternalAssert op.n.sons[0].kind == nkEffectList InternalAssert op.n.sons[0].kind == nkEffectList
var effectList = op.n.sons[0] var effectList = op.n.sons[0]
if effectList.len == 0: if a.kind == nkSym and a.sym.kind == skMethod:
if isForwardedProc(n.sons[0]): let spec = raisesSpec(a.sym.ast.sons[pragmasPos])
let spec = raisesSpec(n.sons[0].sym.ast.sons[pragmasPos]) if not isNil(spec):
mergeEffects(tracked, spec, useLineInfo=false)
else:
addEffect(tracked, createRaise(n))
elif effectList.len == 0:
if isForwardedProc(a):
let spec = raisesSpec(a.sym.ast.sons[pragmasPos])
if not isNil(spec): if not isNil(spec):
mergeEffects(tracked, spec, useLineInfo=false) mergeEffects(tracked, spec, useLineInfo=false)
else: else:
addEffect(tracked, createRaise(n)) addEffect(tracked, createRaise(n))
elif isIndirectCall(n.sons[0]): elif isIndirectCall(a):
addEffect(tracked, createRaise(n)) addEffect(tracked, createRaise(n))
else: else:
effectList = effectList.sons[exceptionEffects] effectList = effectList.sons[exceptionEffects]
@ -204,8 +240,7 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, n.sons[i]) track(tracked, n.sons[i])
# XXX # XXX
# - doc2 should report effects # - more tests
# - check for 'raises' consistency for multi-methods
proc checkRaisesSpec(spec, real: PNode) = proc checkRaisesSpec(spec, real: PNode) =
# 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;
@ -218,6 +253,7 @@ proc checkRaisesSpec(spec, real: PNode) =
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!
localError(spec.info, errInstantiationFrom)
localError(r.info, errGenerated, "can raise an unlisted exception: " & localError(r.info, errGenerated, "can raise an unlisted exception: " &
typeToString(r.typ)) typeToString(r.typ))
# hint about unnecessarily listed exception types: # hint about unnecessarily listed exception types:
@ -238,6 +274,7 @@ proc checkMethodEffects*(disp, branch: PSym) =
for s in 0 .. <spec.len: for s in 0 .. <spec.len:
if inheritanceDiff(r.excType, spec[s].typ) <= 0: if inheritanceDiff(r.excType, spec[s].typ) <= 0:
break search break search
localError(branch.info, errInstantiationFrom)
localError(r.info, errGenerated, "can raise an unlisted exception: " & localError(r.info, errGenerated, "can raise an unlisted exception: " &
typeToString(r.typ)) typeToString(r.typ))

View file

@ -360,7 +360,8 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0]) if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0])
else: result = toVoid else: result = toVoid
of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef, of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkTemplateDef, nkLambdaKinds: nkConverterDef, nkMacroDef, nkTemplateDef, nkLambdaKinds, nkClosure,
nkGotoState, nkState:
result = toVoid result = toVoid
of nkExprColonExpr: of nkExprColonExpr:
result = analyse(c, n.sons[1]) result = analyse(c, n.sons[1])

View file

@ -0,0 +1,20 @@
discard """
line: 16
errormsg: "instantiation from here"
"""
type
TObj = object {.pure, inheritable.}
TObjB = object of TObj
a, b, c: string
EIO2 = ref object of EIO
proc forw: int {. .}
proc lier(): int {.raises: [EIO2].} =
writeln stdout, "arg"
proc forw: int =
raise newException(EIO, "arg")

View file

@ -0,0 +1,20 @@
discard """
line: 13
errormsg: "instantiation from here"
"""
type
TObj = object {.pure, inheritable.}
TObjB = object of TObj
a, b, c: string
EIO2 = ref object of EIO
proc forw: int {.raises: [].}
proc lier(): int {.raises: [EIO].} =
writeln stdout, "arg"
proc forw: int =
raise newException(EIO, "arg")

View file

@ -5,11 +5,13 @@ discard """
type type
Test = object of TObject Test = object of TObject
method doMethod(a: ref TObject) = method doMethod(a: ref TObject) {.raises: [EIO].} =
quit "override" quit "override"
method doMethod(a: ref Test) = method doMethod(a: ref Test) =
echo "hello" echo "hello"
if a == nil:
raise newException(EIO, "arg")
proc doProc(a: ref Test) = proc doProc(a: ref Test) =
echo "hello" echo "hello"

View file

@ -111,5 +111,4 @@ Version 0.9.x
* message passing performance will be greatly improved * message passing performance will be greatly improved
* the syntactic distinction between statements and expressions will be * the syntactic distinction between statements and expressions will be
removed removed
* exception tracking
* the need for forward declarations may be removed * the need for forward declarations may be removed