effects.txt 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. Effect system
  2. =============
  3. Exception tracking
  4. ------------------
  5. Nim supports exception tracking. The `raises`:idx: pragma can be used
  6. to explicitly define which exceptions a proc/iterator/method/converter is
  7. allowed to raise. The compiler verifies this:
  8. .. code-block:: nim
  9. proc p(what: bool) {.raises: [IOError, OSError].} =
  10. if what: raise newException(IOError, "IO")
  11. else: raise newException(OSError, "OS")
  12. An empty ``raises`` list (``raises: []``) means that no exception may be raised:
  13. .. code-block:: nim
  14. proc p(): bool {.raises: [].} =
  15. try:
  16. unsafeCall()
  17. result = true
  18. except:
  19. result = false
  20. A ``raises`` list can also be attached to a proc type. This affects type
  21. compatibility:
  22. .. code-block:: nim
  23. type
  24. Callback = proc (s: string) {.raises: [IOError].}
  25. var
  26. c: Callback
  27. proc p(x: string) =
  28. raise newException(OSError, "OS")
  29. c = p # type error
  30. For a routine ``p`` the compiler uses inference rules to determine the set of
  31. possibly raised exceptions; the algorithm operates on ``p``'s call graph:
  32. 1. Every indirect call via some proc type ``T`` is assumed to
  33. raise ``system.Exception`` (the base type of the exception hierarchy) and
  34. thus any exception unless ``T`` has an explicit ``raises`` list.
  35. However if the call is of the form ``f(...)`` where ``f`` is a parameter
  36. of the currently analysed routine it is ignored. The call is optimistically
  37. assumed to have no effect. Rule 2 compensates for this case.
  38. 2. Every expression of some proc type within a call that is not a call
  39. itself (and not nil) is assumed to be called indirectly somehow and thus
  40. its raises list is added to ``p``'s raises list.
  41. 3. Every call to a proc ``q`` which has an unknown body (due to a forward
  42. declaration or an ``importc`` pragma) is assumed to
  43. raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
  44. 4. Every call to a method ``m`` is assumed to
  45. raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
  46. 5. For every other call the analysis can determine an exact ``raises`` list.
  47. 6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
  48. of ``p`` are taken into consideration.
  49. Rules 1-2 ensure the following works:
  50. .. code-block:: nim
  51. proc noRaise(x: proc()) {.raises: [].} =
  52. # unknown call that might raise anything, but valid:
  53. x()
  54. proc doRaise() {.raises: [IOError].} =
  55. raise newException(IOError, "IO")
  56. proc use() {.raises: [].} =
  57. # doesn't compile! Can raise IOError!
  58. noRaise(doRaise)
  59. So in many cases a callback does not cause the compiler to be overly
  60. conservative in its effect analysis.
  61. Tag tracking
  62. ------------
  63. The exception tracking is part of Nim's `effect system`:idx:. Raising an
  64. exception is an *effect*. Other effects can also be defined. A user defined
  65. effect is a means to *tag* a routine and to perform checks against this tag:
  66. .. code-block:: nim
  67. type IO = object ## input/output effect
  68. proc readLine(): string {.tags: [IO].}
  69. proc no_IO_please() {.tags: [].} =
  70. # the compiler prevents this:
  71. let x = readLine()
  72. A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
  73. also be attached to a proc type. This affects type compatibility.
  74. The inference for tag tracking is analogous to the inference for
  75. exception tracking.
  76. Read/Write tracking
  77. -------------------
  78. **Note**: Read/write tracking is not yet implemented!
  79. The inference for read/write tracking is analogous to the inference for
  80. exception tracking.
  81. Effects pragma
  82. --------------
  83. The ``effects`` pragma has been designed to assist the programmer with the
  84. effects analysis. It is a statement that makes the compiler output all inferred
  85. effects up to the ``effects``'s position:
  86. .. code-block:: nim
  87. proc p(what: bool) =
  88. if what:
  89. raise newException(IOError, "IO")
  90. {.effects.}
  91. else:
  92. raise newException(OSError, "OS")
  93. The compiler produces a hint message that ``IOError`` can be raised. ``OSError``
  94. is not listed as it cannot be raised in the branch the ``effects`` pragma
  95. appears in.