Documentation
Nybl language guide

What's new in Nybl 0.5

What’s new in Nybl 0.5🔗

Nybl 0.5 is a breaking release. All crates move together to 0.5.0:

[dependencies]
nybl = { package = "nybl-lang", version = "0.5" }
nybl-vm = "0.5"       # optional bytecode engine
nybl-compile = "0.5"  # optional AOT transpiler
nybl-sys = "0.5"      # optional OS-backed host

The release bounds the native stack an execution can use, so a host can size its threads from Nybl’s limits; shares resource accounting between a program and the modules it loads; and turns flat-import name clashes from warnings into errors.

Import name clashes are errors🔗

In 0.4, two glob imports that introduced the same name kept the first binding and printed a warning to stderr. In 0.5 a glob or selective use that would bind a name the scope already binds to a different declaration — a let, parameter, module alias, fn, type, built-in type, or another import — fails:

use money
use ledger           // both export `helper`
error: `helper` is already defined — `use ledger` would import a different `helper`

A fn declared after a flat use bound its name fails too: `helper` is already imported from `money` — `fn helper` would replace it.

Importing the same declaration twice (a diamond re-export, or a selective import followed by a glob of the same module) is fine, a later let still shadows an import, and use path as alias is unaffected. The runtime warning is gone; static-check warnings are unchanged. See Modules → Name clashes.

Chains count toward the parse cap🔗

The parser’s 128-level nesting cap now counts each link of an operator or postfix chain — a + b + ..., x[0][0]..., .m().m()..., f(1)(2).... Code past the cap is rejected with Code is nested too deeply. Programs with very long chains may need splitting across let bindings. See Parse and nesting limits.

Nesting budget🔗

NyblLimits has a new field, max_nesting (default nybl::DEFAULT_MAX_NESTING, 2048, in both presets):

let limits = NyblLimits::standard().with_max_nesting(512);

Every active function, lambda, or method body, loading module top level, and the root program charge their static nesting depth. Exceeding the budget is an ordinary, try_call-catchable error, Too much nested code across active function calls, raised at the same call in the walker, VM, and sandboxed AOT.

Modules share the run’s limits🔗

A module load is one call frame. Call depth (64), the nesting budget, and the step budget are shared between an importer and the modules it loads in every engine; the VM previously restarted its step counter in each module. Loading a module also costs one step per token of its source, charged at the use line the first time it is imported. See Modules share the limits.

Bounded native stack🔗

With the parse cap, call-depth cap, and nesting budget together, a thread with a 10 MiB stack (release builds) or 128 MiB (debug builds) is enough for any program under default limits. Embedders should run scripts on threads at least that large; nybl run and the REPL now do. See Thread stack size.

Migrating from 0.4🔗

  • Change dependency requirements from "0.4" to "0.5".
  • Resolve the name clashes the new errors report: import only the names you need (use money.{total}), namespace the module (use money as m), or rename the local declaration.
  • Add max_nesting to any NyblLimits { .. } literal that lists every field, or spread ..NyblLimits::standard().
  • Run scripts on a thread of the documented stack size.
  • Split expressions whose operator or postfix chains exceed 128 links.
  • Hand-built nybl_vm bytecode must set the new Chunk::nesting_depth and FnDef::checks_import_clash fields.

The repository’s CHANGELOG.md lists the full release notes.

Found something unclear?Edit this page on GitHub
Documentation

Search Nybl

Start typing to search the guide and reference.