diff options
-rw-r--r-- | compiler/sempass2.nim | 13 | ||||
-rw-r--r-- | doc/manual.txt | 7 | ||||
-rw-r--r-- | tests/reject/teffects5.nim | 14 |
3 files changed, 28 insertions, 6 deletions
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 9213bd48d..b78e36531 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -79,6 +79,7 @@ type exc: PNode # stack of exceptions tags: PNode # list of tags bottom: int + owner: PSym PEffects = var TEffects @@ -173,11 +174,16 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = track(tracked, b.sons[blen-1]) tracked.bottom = oldBottom -proc isIndirectCall(n: PNode): bool = +proc isIndirectCall(n: PNode, owner: PSym): bool = # we don't count f(...) as an indirect call if 'f' is an parameter. # Instead we track expressions of type tyProc too. See the manual for # details: - result = n.kind != nkSym or n.sym.kind notin (routineKinds+{skParam}) + if n.kind != nkSym: + result = true + elif n.sym.kind == skParam: + result = owner != n.sym.owner or owner == nil + elif n.sym.kind notin routineKinds: + result = true proc isForwardedProc(n: PNode): bool = result = n.kind == nkSym and sfForward in n.sym.flags @@ -274,7 +280,7 @@ proc track(tracked: PEffects, n: PNode) = elif effectList.len == 0: if isForwardedProc(a): propagateEffects(tracked, n, a.sym) - elif isIndirectCall(a): + elif isIndirectCall(a, tracked.owner): addEffect(tracked, createRaise(n)) addTag(tracked, createTag(n)) else: @@ -355,6 +361,7 @@ proc trackProc*(s: PSym, body: PNode) = var t: TEffects t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] + t.owner = s track(t, body) let p = s.ast.sons[pragmasPos] diff --git a/doc/manual.txt b/doc/manual.txt index 22aa22266..e6fe5fbdb 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2910,7 +2910,8 @@ possibly raised exceptions; the algorithm operates on ``p``'s call graph: raise ``system.E_Base`` (the base type of the exception hierarchy) and thus any exception unless ``T`` has an explicit ``raises`` list. However if the call is of the form ``f(...)`` where ``f`` is a parameter - is ignored. Rule 2 compensates for this case. + of the currently analysed routine it is ignored. The call is optimistically + assumed to have no effect. Rule 2 compensates for this case. 2. Every expression of some proc type wihtin a call that is not a call itself (and not nil) is assumed to be called indirectly somehow and thus its raises list is added to ``p``'s raises list. @@ -2933,9 +2934,9 @@ Rules 1-2 ensure the following works: proc doRaise() {.raises: [EIO].} = raise newException(EIO, "IO") - proc use() = + proc use() {.raises: [].} = + # doesn't compile! Can raise EIO! noRaise(doRaise) - # Here the compiler inferes that EIO can be raised. So in many cases a callback does not cause the compiler to be overly conservative in its effect analysis. diff --git a/tests/reject/teffects5.nim b/tests/reject/teffects5.nim new file mode 100644 index 000000000..42be115c3 --- /dev/null +++ b/tests/reject/teffects5.nim @@ -0,0 +1,14 @@ +discard """ + errormsg: 'type mismatch' + line: 7 +""" + +proc p(q: proc() ): proc() {.tags: [], raises: [], closure.} = + return proc () = + q() + +let yay = p(proc () = raise newException(EIO, "IO")) + +proc main() {.raises: [], tags: [].} = yay() + +main() |