异常跟踪
Nim支持异常跟踪。 raises 编译器可用于显式定义允许proc/iterator/method/converter引发的异常。编译器验证这个:
- proc p(what: bool) {.raises: [IOError, OSError].} =
- if what: raise newException(IOError, "IO")
- else: raise newException(OSError, "OS")
一个空的 raises 列表( raises:[] )意味着不会引发任何异常:
- proc p(): bool {.raises: [].} =
- try:
- unsafeCall()
- result = true
- except:
- result = false
raises 列表也可以附加到proc类型。这会影响类型兼容性:
- type
- Callback = proc (s: string) {.raises: [IOError].}
- var
- c: Callback
- proc p(x: string) =
- raise newException(OSError, "OS")
- 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 列表,考虑 p 的 raise 和 try 语句。
规则1-2确保下面的代码正常工作:
- proc noRaise(x: proc()) {.raises: [].} =
- # 可能引发任何异常的未知调用, 但这是合法的:
- x()
- proc doRaise() {.raises: [IOError].} =
- raise newException(IOError, "IO")
- proc use() {.raises: [].} =
- # 不能编译, 可能引发IOError。
- noRaise(doRaise)
因此,在许多情况下,回调不会导致编译器在其效果分析中过于保守。