summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-05-11 10:53:40 +0200
committerAraq <rumpf_a@web.de>2013-05-11 10:53:40 +0200
commitadedfc3a10ca6fb4b3c2f467b2d1f36da678c164 (patch)
treee7cf48d6e80f19cabcd61bda60823593893060dc
parent1980c8930d69ec3a53bcb4da993d387269f78eab (diff)
downloadNim-adedfc3a10ca6fb4b3c2f467b2d1f36da678c164.tar.gz
new effect system should be sound now
-rw-r--r--compiler/sempass2.nim13
-rw-r--r--doc/manual.txt7
-rw-r--r--tests/reject/teffects5.nim14
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()