mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-30 01:44:37 +00:00
130 lines
4.0 KiB
Plaintext
130 lines
4.0 KiB
Plaintext
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.
|