Implementation Limits

There are several hard limits built in to GameLisp.

There cannot be more than 16,777,216 (2^24) distinct symbols. Running out of symbols is treated as an unrecoverable error, similar to an out-of-memory condition: attempting to allocate additional symbols will trigger a panic.

There can't be more than 256 simultaneous GameLisp function calls on the callstack. Attempting to call the 257th function will trigger an error instead. This is because:

  • GameLisp implements recursion using Rust's callstack.
  • A Rust stack overflow would abort the process.
  • GameLisp function calls use up quite a lot of stack space.
  • The default stack size for *-pc-windows-msvc targets is only one megabyte.

Rust currently seems to use up a very large amount of stack space in debug builds. If you try to run the glsp crate at opt-level = 0, you may still encounter stack overflows, even when there are only a few dozen GameLisp function calls on the callstack.

Each "frame" (fn body or single toplevel form) may only contain 256 "registers" (parameters, local variables, scratch registers or literals). Exceeding this limit will trigger an error. This will usually only happen if you use macros to code-generate an extremely long function. In that case, you can break up the function into multiple frames by wrapping each part of its body in a separate fn form.

A class form may not contain more than 31 state or state* forms, including nested states and states defined by mixins.

When a function call has more than 32 arguments, the 33rd and later arguments can't be splayed.

No more than 256 Runtimes may simultaneously exist within a single thread.

Each Runtime may not contain more than 8,388,606 (2^23 - 2) rooted objects. (This is the combined total of objects which have been strongly rooted, with Root, and those which have been weakly rooted, with Gc). There's also a soft limit on strongly-rooted objects, because they each consume a few nanoseconds of time whenever the garbage collector is invoked. If you're likely to require more than 100,000 strongly-rooted objects, consider storing that data in Rust rather than GameLisp.

There are a small number of ways that a Root or Gc may outlive its parent Runtime, or be assigned to a different Runtime. In either scenario, the only way that GameLisp can uphold memory safety is by immediately aborting the process! The most likely way you might do this accidentally is by either leaking a Root (using a function like Box::leak or Rc::new), or storing a Root in a thread_local! variable.