#855·codon

Exception handing in JIT: unwind-based vs. return-based approach?

Author: LeeLee26Created Aug 31, 2026Updated Sep 4, 2026

Hi there!

I'd like to open a discussion around the current exception-handling mechanism under the JIT path, and propose an alternative that could simplify both the implementation and future extensibility.

Current Behavior

When an unhandled Codon exception is raised in a JIT-compiled function, the control flow goes through two layers of exception throwing:

Codon exception (e.g. raise IndexError)
  → seq_throw()
  → _Unwind_RaiseException(SEQ_EXCEPTION_CLASS)
  → no matching handler found → fall through
  → seq_terminate()
     → AOT path: print diagnostics and call abort()
     → JIT path: throw runtime::JITError

For example, the following snippet:

python
import codon

@codon.jit
def func():
    x = []
    x.pop()    # raise IndexError

func()

produces output like:

Traceback (most recent call last):
  ...
  ...codon_jit.JITError: IndexError: /xxx/stdlib/internal/types/collections/list.codon:337:13: pop from empty list

Issues with the Current Approach

  1. Implicit assumption of no handler. The first _Unwind_RaiseException call implicitly relies on the absence of any other codon-exception catch handler so that execution eventually reaches seq_terminate (the same assumption holds in the AOT path as well). This makes it significantly harder to integrate foreign exception-handling mechanisms — for instance, adding pybind11 bindings that need to intercept and translate Codon exceptions into Python exceptions.
  2. Personality routine complexity. If we want to propagate typed exceptions (rather than just a single opaque JITError), we would need to implement a full personality routine. Supporting such a routine correctly across different platforms (macOS / Linux / Windows, various unwinder ABIs) introduces considerable complexity.

Proposed Alternative: Explicit Return-Based Error Propagation

Drawing inspiration from Rust's approach, I'd like to propose that — at least for the JIT path — the Codon compiler lower raise, try/except/finally, and exception propagation into an explicit CFG with a status-return calling convention, eliminating the need for stack unwinding entirely.

Conceptual Model

Every Codon function that may raise an exception would be lowered to a noexcept function returning a status code:

cpp
enum SeqStatus : int32_t {
    SEQ_OK    = 0,
    SEQ_ERROR = 1,
};

SeqStatus function(
    ReturnType    *result,
    SeqException **error,
    Arguments...
) noexcept;

For example, the logical Codon code:

python
def divide(a: int, b: int) -> int:
    if b == 0:
        raise ZeroDivisionError()
    return a // b

would be lowered to something equivalent to:

cpp
SeqStatus divide(int64_t *result, SeqException **error,
                 int64_t a, int64_t b) noexcept {
    if (b == 0) {
        *error = makeZeroDivisionError();
        return SEQ_ERROR;
    }
    *result = a / b;
    return SEQ_OK;
}

And the caller site:

cpp
SeqException *error = nullptr;
int64_t result;

if (divide(&result, &error, a, b) != SEQ_OK)
    return propagate(error);

This is semantically identical to Rust's:

rust
let result = divide(a, b)?;

Why This Could Be Worth It

  • Simplicity. No dependency on platform-specific unwind libraries or personality routines — just ordinary function calls and branches.
  • Portability. The mechanism is pure C-level ABI; it works identically on every target without platform-specific unwind support.
  • Extensibility. Foreign code (e.g., a pybind11 bridge or others) can inspect the SeqException* directly and translate it into whatever exception model the host language expects, with no need to intercept or re-raise unwind exceptions.
  • Performance. The overhead of an extra return-value check on the happy path is minimal and may, in practice, be comparable to — or even lower than — the cost of maintaining landing pads and LSDA tables required by the current unwind-based approach.

I'd love to hear the maintainers' and the community's thoughts on this. Happy to contribute to the implementation if there's interest!

Thanks for your time.