123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- Effect system
- =============
- Exception tracking
- ------------------
- Nim supports exception tracking. The `raises`:idx: pragma can be used
- to explicitly define which exceptions a proc/iterator/method/converter is
- allowed to raise. The compiler verifies this:
- .. code-block:: nim
- proc p(what: bool) {.raises: [IOError, OSError].} =
- if what: raise newException(IOError, "IO")
- else: raise newException(OSError, "OS")
- An empty ``raises`` list (``raises: []``) means that no exception may be raised:
- .. code-block:: nim
- proc p(): bool {.raises: [].} =
- try:
- unsafeCall()
- result = true
- except:
- result = false
- A ``raises`` list can also be attached to a proc type. This affects type
- compatibility:
- .. code-block:: nim
- type
- Callback = proc (s: string) {.raises: [IOError].}
- var
- c: Callback
- proc p(x: string) =
- raise newException(OSError, "OS")
- c = p # type error
- For a routine ``p`` the compiler uses inference rules to determine the set of
- possibly raised exceptions; the algorithm operates on ``p``'s call graph:
- 1. Every indirect call via some proc type ``T`` is assumed to
- raise ``system.Exception`` (the base type of the exception hierarchy) and
- thus any exception unless ``T`` has an explicit ``raises`` list.
- However if the call is of the form ``f(...)`` where ``f`` is a parameter
- of the currently analysed routine it is ignored. The call is optimistically
- assumed to have no effect. Rule 2 compensates for this case.
- 2. Every expression of some proc type within a call that is not a call
- itself (and not nil) is assumed to be called indirectly somehow and thus
- its raises list is added to ``p``'s raises list.
- 3. Every call to a proc ``q`` which has an unknown body (due to a forward
- declaration or an ``importc`` pragma) is assumed to
- raise ``system.Exception`` unless ``q`` has an explicit ``raises`` list.
- 4. Every call to a method ``m`` is assumed to
- raise ``system.Exception`` unless ``m`` has an explicit ``raises`` list.
- 5. For every other call the analysis can determine an exact ``raises`` list.
- 6. For determining a ``raises`` list, the ``raise`` and ``try`` statements
- of ``p`` are taken into consideration.
- Rules 1-2 ensure the following works:
- .. code-block:: nim
- proc noRaise(x: proc()) {.raises: [].} =
- # unknown call that might raise anything, but valid:
- x()
- proc doRaise() {.raises: [IOError].} =
- raise newException(IOError, "IO")
- proc use() {.raises: [].} =
- # doesn't compile! Can raise IOError!
- noRaise(doRaise)
- So in many cases a callback does not cause the compiler to be overly
- conservative in its effect analysis.
- Tag tracking
- ------------
- The exception tracking is part of Nim's `effect system`:idx:. Raising an
- exception is an *effect*. Other effects can also be defined. A user defined
- effect is a means to *tag* a routine and to perform checks against this tag:
- .. code-block:: nim
- type IO = object ## input/output effect
- proc readLine(): string {.tags: [IO].}
- proc no_IO_please() {.tags: [].} =
- # the compiler prevents this:
- let x = readLine()
- A tag has to be a type name. A ``tags`` list - like a ``raises`` list - can
- also be attached to a proc type. This affects type compatibility.
- The inference for tag tracking is analogous to the inference for
- exception tracking.
- Read/Write tracking
- -------------------
- **Note**: Read/write tracking is not yet implemented!
- The inference for read/write tracking is analogous to the inference for
- exception tracking.
- Effects pragma
- --------------
- The ``effects`` pragma has been designed to assist the programmer with the
- effects analysis. It is a statement that makes the compiler output all inferred
- effects up to the ``effects``'s position:
- .. code-block:: nim
- proc p(what: bool) =
- if what:
- raise newException(IOError, "IO")
- {.effects.}
- else:
- raise newException(OSError, "OS")
- The compiler produces a hint message that ``IOError`` can be raised. ``OSError``
- is not listed as it cannot be raised in the branch the ``effects`` pragma
- appears in.
|