nim-lang / nif

NIF is a text based data format designed for compiler frontend/backend communication or communication between different programming languages.
MIT License
46 stars 4 forks source link

Exception handling primitives #123

Closed Araq closed 1 week ago

Araq commented 2 weeks ago

try

There is a construct called try that has the format (try <actions> <onerr> <finally). It always has exactly 3 children. If NIFC encounters this construct, it is always translated into C++'s equivalent. It is the job of the NIFC caller not to emit it when C++ support is disabled. If C++ code is not generated, the new (err) and (onerr) constructs have to be used which can be mapped easily to the current exceptions:goto mode.

raise

(raise value) can be used to produce C++'s throw statement.

onerr .

Calls that can potentially raise an exception must be called with (onerr). When C++'s exception handling mode is used, onerr's first child must always be the dot .:

(try
  (stmts
   (let :x (onerr . create))
   (call consume x)
  (call wasMoved (addr x))
  )
  . # empty `except` section
  (call destroy x)
)

err

There is a special magic variable called (err) of type bool that can be set via (asgn) and queried like other locations: (asgn (err) (true)) # set the error bit

onerr

Calls that trigger exception handling control flow must called with (onerr). The format is (onerr <action> <f> <args>). Typically action is a jmp instruction:

  (stmts
   (let :x (onerr (jmp L1) create))
  (call printf "success!")
  (lab :L1))

raises and errs pragma

Functions can and must (!) be annotated with a (raises) pragma to indicate that they can raise a C++ exception. Likewise they need to use the errs pragma if they use the (err) mechanism. A function can use both annotations at the same time. That would be a C++ function that uses both (raise) and (err).

Benefits of this design

  1. Can leverage C++'s zero-cost exception model.
  2. Can be mapped to LLVM's exception handling model.
  3. Can be mapped to exceptions:goto via:
    • thread local storage for the error bit
    • CPU flag for the error bit
    • Change the function's prototype to add err: var bool to the parameter list or bool to the return type.
  4. Is entirely orthogonal to the used Runtime type information mechanism (RTTI) and also works without RTTI.
  5. Supports the exceptions:quirky mode via an empty node for onerr, onerr . f args signals that f can raise but does not produce any code besides the function call.
ringabout commented 1 week ago

implemented by https://github.com/nim-lang/nif/pull/126