next steps for exception tracking
This commit is contained in:
parent
224f42bbd7
commit
9f38ff0c65
7 changed files with 73 additions and 34 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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!)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue