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
    TCallback = proc (s: string) {.raises: [IOError].}
  var
    c: TCallback

  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 wihtin 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.
