Lyric
A statically typed application language with a bytecode VM — standalone, and embeddable in a host with a capability-based sandbox.
What changed in v4.0.1 — 2026-08-27
The sweep after 4.0.0. Eight probes against what the major added; four real findings, all fixed failing-test-first. Nothing in the language or the format changed.
Fixed
- A read of a CLOSED socket no longer crashes the process.
netReadReadyanswered "no bytes" for a descriptor the table does not know without recording a reason, so the previous call's would-block stood andreadSomewent back to the scheduler to wait on a descriptor nothing could ever ready.pollthen selected on a list that resolved to nothing, andSocket.Selectthrew — an unhandled host exception, printed as a .NET stack trace, which is precisely what the panic doctrine exists to prevent. Now: an unresolvable descriptor is a recorded failure (so the silent form answersnulland the twin names it), andpollnames a dead descriptor READY at once — the convention an errored socket already followed, since the wake exists so the next read can tell the waiter what happened. - An import used through its SECOND overload is no longer reported as unused
(
LYR-SEM0072). Importing a name brings the whole set, but the accounting looked only at its first member — soimport std.io.net { bind, localPort, close };warned about names the file demonstrably calls, and under--deny-warningsthat breaks a build for correct code. - One VM's
interrupt()no longer reaches another VM's tasks. The pending flag was process-wide, while the socket table beside it is per-thread for exactly the opposite reason. The SIGNAL stays process-wide — SIGINT means the process — but the programmatic raise is now per VM. - A second VM no longer clears the first one's interrupt listening. The flag was assigned on every poll, so a VM without a parked task turned Ctrl+C back into a process kill while another VM was waiting for it. Listening is counted now, and the count moves on transitions.
- A sleep beyond ~292 million years no longer waits forever. The millisecond-to-microsecond
conversion overflowed into a negative number, which
Selectreads as "no deadline"; the clamp now happens before the multiplication.