summary refs log tree commit diff stats
path: root/compiler/sempass2.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/sempass2.nim')
-rw-r--r--compiler/sempass2.nim91
1 files changed, 73 insertions, 18 deletions
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index bdc4e7028..89d8d45d8 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -8,7 +8,8 @@
 #
 
 import
-  ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg
+  intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, 
+  wordrecg
 
 # Second semantic checking pass over the AST. Necessary because the old
 # way had some inherent problems. Performs:
@@ -48,8 +49,6 @@ when false:
   proc sem2call(c: PContext, n: PNode): PNode =
     assert n.kind in nkCallKinds
     
-    
-
   proc sem2sym(c: PContext, n: PNode): PNode =
     assert n.kind == nkSym
   
@@ -97,16 +96,17 @@ proc excType(n: PNode): PType =
           else: n.sons[0].typ
   result = skipTypes(t, skipPtrs)
 
-proc addEffect(a: PEffects, e: PNode) =
+proc addEffect(a: PEffects, e: PNode, useLineInfo=true) =
   assert e.kind == nkRaiseStmt
   var aa = a.exc
   for i in a.bottom .. <aa.len:
-    if sameType(aa[i].excType, e.excType) and aa[i].info == e.info: return
+    if sameType(aa[i].excType, e.excType):
+      if not useLineInfo: return
+      elif aa[i].info == e.info: return
   throws(a, e)
 
-proc mergeEffects(a: PEffects, b: PNode) =
-  for effect in items(b):
-    addEffect(a, effect)
+proc mergeEffects(a: PEffects, b: PNode, useLineInfo) =
+  for effect in items(b): addEffect(a, effect, useLineInfo)
 
 proc listEffects(a: PEffects) =
   var aa = a.exc
@@ -118,12 +118,13 @@ proc catches(tracked: PEffects, e: PType) =
   var L = tracked.exc.len
   var i = tracked.bottom
   while i < L:
-    # e supertype of r?
-    if inheritanceDiff(e, tracked.exc[i].excType) <= 0:
+    # r supertype of e?
+    if inheritanceDiff(tracked.exc[i].excType, e) <= 0:
       tracked.exc.sons[i] = tracked.exc.sons[L-1]
       dec L
     else:
       inc i
+  setLen(tracked.exc.sons, L)
   
 proc catchesAll(tracked: PEffects) =
   setLen(tracked.exc.sons, tracked.bottom)
@@ -161,6 +162,20 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
       # list the computed effects up to here:
       listEffects(tracked)
 
+proc raisesSpec(n: PNode): PNode =
+  for i in countup(0, sonsLen(n) - 1):
+    var it = n.sons[i]
+    if it.kind == nkExprColonExpr and whichPragma(it) == wRaises:
+      result = it.sons[1]
+      if result.kind notin {nkCurly, nkBracket}:
+        result = newNodeI(nkCurly, result.info)
+        result.add(it.sons[1])
+      return
+
+proc createRaise(n: PNode, t: PType): PNode =
+  result = newNodeI(nkRaiseStmt, n.info)
+  result.add(newNodeIT(nkType, n.info, t))
+
 proc track(tracked: PEffects, n: PNode) =
   case n.kind
   of nkRaiseStmt: throws(tracked, n)
@@ -168,18 +183,20 @@ proc track(tracked: PEffects, n: PNode) =
     # p's effects are ours too:
     let op = n.sons[0].typ
     if op != nil and op.kind == tyProc:
-      InternalAssert op.kind == tyProc and op.n.sons[0].kind == nkEffectList
+      InternalAssert op.n.sons[0].kind == nkEffectList
       var effectList = op.n.sons[0]
       if effectList.len == 0:
-        if isIndirectCall(n.sons[0]) or isForwardedProc(n.sons[0]):
-          # assume the worst: raise of exception 'E_Base':
-          var rs = newNodeI(nkRaiseStmt, n.info)
-          var re = newNodeIT(nkType, n.info, sysTypeFromName"E_Base")
-          rs.add(re)
-          addEffect(tracked, rs)
+        if isForwardedProc(n.sons[0]):
+          let spec = raisesSpec(n.sons[0].sym.ast.sons[pragmasPos])
+          if not isNil(spec):
+            mergeEffects(tracked, spec, useLineInfo=false)
+          else:
+            addEffect(tracked, createRaise(n, sysTypeFromName"E_Base"))
+        elif isIndirectCall(n.sons[0]):
+          addEffect(tracked, createRaise(n, sysTypeFromName"E_Base"))
       else:
         effectList = effectList.sons[exceptionEffects]
-        mergeEffects(tracked, effectList)
+        mergeEffects(tracked, effectList, useLineInfo=true)
   of nkTryStmt:
     trackTryStmt(tracked, n)
     return
@@ -191,10 +208,45 @@ proc track(tracked: PEffects, n: PNode) =
   for i in 0 .. <safeLen(n):
     track(tracked, n.sons[i])
 
+# XXX
+# - make use of 'raises' in proc types compatibility
+# - check for 'raises' consistency for multi-methods
+
+proc checkRaisesSpec(spec, real: PNode) =
+  # check that any real exception is listed in 'spec'; mark those as used;
+  # report any unused exception
+  var used = initIntSet()
+  for r in items(real):
+    block search:
+      for s in 0 .. <spec.len:
+        # r supertype of s?
+        if inheritanceDiff(r.excType, spec[s].typ) <= 0:
+          used.incl(s)
+          break search
+      # XXX call graph analysis would be nice here!
+      localError(r.info, errGenerated, "can raise an unlisted exception: " &
+        typeToString(r.sons[0].typ))
+  # hint about unnecessarily listed exception types:
+  for s in 0 .. <spec.len:
+    if not used.contains(s):
+      Message(spec[s].info, hintXDeclaredButNotUsed, renderTree(spec[s]))
+
+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 effectList = formal.n.sons[0]
+  if effectList.len == 0:
+    # 'formal' has no restrictions :-)
+    result = true
+
 proc trackProc*(s: PSym, body: PNode) =
   var effects = s.typ.n.sons[0]
   InternalAssert effects.kind == nkEffectList
   # effects already computed?
+  if sfForward in s.flags: return
   if effects.len == effectListLen: return
   newSeq(effects.sons, effectListLen)
   effects.sons[exceptionEffects] = newNodeI(nkArgList, body.info)
@@ -203,3 +255,6 @@ proc trackProc*(s: PSym, body: PNode) =
   t.exc = effects.sons[exceptionEffects]
   track(t, body)
   
+  let spec = raisesSpec(s.ast.sons[pragmasPos])
+  if not isNil(spec):
+    checkRaisesSpec(spec, t.exc)