• 异常跟踪

    异常跟踪

    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)

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