异常跟踪

Nim支持异常跟踪。 raises 编译器可用于显式定义允许proc/iterator/method/converter引发的异常。编译器验证这个:

  1. proc p(what: bool) {.raises: [IOError, OSError].} =
  2. if what: raise newException(IOError, "IO")
  3. else: raise newException(OSError, "OS")

一个空的 raises 列表( raises:[] )意味着不会引发任何异常:

  1. proc p(): bool {.raises: [].} =
  2. try:
  3. unsafeCall()
  4. result = true
  5. except:
  6. result = false

raises 列表也可以附加到proc类型。这会影响类型兼容性:

  1. type
  2. Callback = proc (s: string) {.raises: [IOError].}
  3. var
  4. c: Callback
  5.  
  6. proc p(x: string) =
  7. raise newException(OSError, "OS")
  8.  
  9. c = p # 类型错误

对于例程 p ,编译器使用推理规则来确定可能引发的异常集;算法在 p 的调用图上运行:

  • 1.通过某些proc类型 T 的每个间接调用都被假定为引发 system.Exception (异常层次结构的基本类型),因此除非 T 有明确的 raises 列表。
  • 但是如果调用的形式是 f(…) 其中 f 是当前分析的例程的参数,则忽略它。 乐观地认为该呼叫没有效果。规则2补偿了这种情况。

2.假定在一个不是调用本身(而不是nil)的调用中的某些proc类型的每个表达式都以某种方式间接调用,因此它的引发列表被添加到 p 的引发列表中。 3.对前向声明或 importc 编译指示的未知proc q 的每次调用,假定会引发 system.Exception ,除非 q 有一个明确的 raises 列表。 4.每次对方法 m 的调用都会被假定为引发 system.Exception ,除非 m 有一个明确的 raises 列表。 5.对于每个其他调用,分析可以确定一个确切的 raises 列表。 6.为了确定 raises 列表,考虑 praisetry 语句。

规则1-2确保下面的代码正常工作:

  1. proc noRaise(x: proc()) {.raises: [].} =
  2. # 可能引发任何异常的未知调用, 但这是合法的:
  3. x()
  4.  
  5. proc doRaise() {.raises: [IOError].} =
  6. raise newException(IOError, "IO")
  7.  
  8. proc use() {.raises: [].} =
  9. # 不能编译, 可能引发IOError。
  10. noRaise(doRaise)

因此,在许多情况下,回调不会导致编译器在其效果分析中过于保守。