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 hostThe 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_nestingto anyNyblLimits { .. }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_vmbytecode must set the newChunk::nesting_depthandFnDef::checks_import_clashfields.
The repository’s CHANGELOG.md lists the full release notes.