<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Voxlang releases</title>
  <subtitle>Voxlang is a small systems compiler. Sentence-based English in, native assembly out, with no runtime, no virtual machine and no standard library.</subtitle>
  <id>https://vox-lang.dev/feed.xml</id>
  <link rel="self" type="application/atom+xml" href="https://vox-lang.dev/feed.xml"/>
  <link rel="alternate" type="text/html" href="https://vox-lang.dev/"/>
  <updated>2026-09-06T00:00:00Z</updated>
  <author><name>Josjuar Lister</name><email>info@vox-lang.dev</email></author>
  <generator uri="https://vox-lang.dev/">CHANGELOG.md at 5a73624, 2026-10-05</generator>
  <entry>
    <title>Vox 0.4.15</title>
    <id>tag:vox-lang.dev,2026:release/0.4.15</id>
    <updated>2026-09-06T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.15"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A value whose type is only known while the program runs now converts to fit, so a mixed list or map value can no longer crash a program. Plus 13 more changes.</summary>
    <content type="html">&lt;p&gt;A value whose type is only known while the program runs now converts to fit, so a mixed list or map value can no longer crash a program.&lt;/p&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A dynamically-typed value read into a typed variable now casts everywhere, not just at a map read.&lt;/strong&gt; #114 covered a map-key read only; a &lt;code&gt;value&lt;/code&gt; variable, an element/first/last off a list proven mixed, and a call to a &lt;code&gt;value&lt;/code&gt;-returning function now get the same runtime-tag cast, or the error flag on an undefined cast, instead of copying raw bits. Closes the whole class of SIGSEGVs and stray pointer prints #114&amp;apos;s narrower fix left open (#115).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A single-quoted one-word name now resolves inside a &lt;code&gt;{...}&lt;/code&gt; format-string slot, exactly as it already did everywhere else.&lt;/strong&gt; &lt;code&gt;Print &amp;quot;{&amp;apos;tally&amp;apos;}&amp;quot;&lt;/code&gt; used to fail with &amp;quot;Unknown variable: &amp;apos;tally&amp;apos;&amp;quot; for every variable type — the slot parser fell back to using the placeholder&amp;apos;s raw text, quote marks included, as the variable name whenever the quoted name had no space in it. A quoted name followed by a property (&lt;code&gt;{&amp;apos;toolbox&amp;apos;s size}&lt;/code&gt;) or a multi-word quoted name (&lt;code&gt;{&amp;apos;the toolbox&amp;apos;}&lt;/code&gt;) already worked, since both contain a space and were already routed through the real lexer; the fix routes the one-word case through the same lexer instead of special-casing it (#110).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Free&lt;/code&gt; on a list now actually releases it, everywhere.&lt;/strong&gt; A global list was a silent no-op (the codegen branch only looked in the local stack frame, never the global mirror); a function-local list&amp;apos;s block was genuinely released but the variable was left pointing at it, so the next read segfaulted. A list now gets the same released-buffer contract a buffer already has — empty, every write refused with the error flag, a second &lt;code&gt;Free&lt;/code&gt; a no-op that flags — and, freeing a list also recursively frees every nested list or map it holds (#109).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A list or map placed inside another list or map is now copied, not shared.&lt;/strong&gt; A nested collection previously aliased the same block: a list-literal element, an &lt;code&gt;append&lt;/code&gt;ed collection, a map value, and every way of reading a nested collection back out (&lt;code&gt;element N of&lt;/code&gt;, &lt;code&gt;&amp;apos;s first&lt;/code&gt;/&lt;code&gt;last&lt;/code&gt;, a map value, a &lt;code&gt;For each&lt;/code&gt; loop variable) all handed back the SAME pointer, so mutating one side reached the other, and freeing the outer one could dangle a separately-named variable. Every one of those sites now copies (GitHub #34, Option 1) (#111).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A typed function that falls off its end now hands back a real, usable empty value for every declared type, not just four of them.&lt;/strong&gt; A conditional &lt;code&gt;Return&lt;/code&gt; nested in a branch that never fires used to leave the implicit epilogue holding whatever the last computation left in &lt;code&gt;rax&lt;/code&gt; — safe only for &lt;code&gt;number&lt;/code&gt;/&lt;code&gt;float&lt;/code&gt;/&lt;code&gt;boolean&lt;/code&gt;/&lt;code&gt;text&lt;/code&gt;/&lt;code&gt;value&lt;/code&gt;. A &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;buffer&lt;/code&gt; return dereferenced that stray value and segfaulted; a &lt;code&gt;map&lt;/code&gt; return walked it as a bogus header and hung; a &lt;code&gt;thing&lt;/code&gt; return handed back real, valid storage that was never actually written, so it read the caller&amp;apos;s leftover stack instead of the type&amp;apos;s declared defaults. &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;, and &lt;code&gt;thing&lt;/code&gt; now fall off the end exactly as the manual already promised for every other type: a real empty &lt;code&gt;[]&lt;/code&gt;/&lt;code&gt;{}&lt;/code&gt;/buffer that still takes &lt;code&gt;append&lt;/code&gt;/a key set, the zero time, or the thing&amp;apos;s all-defaults instance (#112).&lt;/li&gt;&lt;li&gt;Keywords chapter lists every reserved word, checked by a new drift-guard test (#106 second half, GitHub #239).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Reading a dynamic map value into a typed variable now casts it to that type instead of crashing.&lt;/strong&gt; A map built with &lt;code&gt;Set&lt;/code&gt; (or a literal map holding more than one value type) carries a runtime type tag the compiler cannot see statically; a &lt;code&gt;text called t is m&amp;apos;s &amp;quot;k&amp;quot;.&lt;/code&gt; where &lt;code&gt;&amp;quot;k&amp;quot;&lt;/code&gt; held a number used to copy the number&amp;apos;s raw bits into the text slot, which segfaulted on first use. It is now cast to the destination type exactly as an explicit &lt;code&gt;... as a text&lt;/code&gt; would (owner ruling); a cast the language does not define (a number into a &lt;code&gt;list&lt;/code&gt;/&lt;code&gt;map&lt;/code&gt;, or the reverse) raises the error flag instead of crashing (#114).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Redeclaring a global as a different kind now names the conflict at the redeclaration, not &amp;quot;Unknown variable&amp;quot; at some later read.&lt;/strong&gt; &lt;code&gt;a list called kept is [].&lt;/code&gt; followed later by &lt;code&gt;a buffer called kept is 16 bytes in size.&lt;/code&gt; used to report &lt;code&gt;Unknown variable: kept&lt;/code&gt; at the first function that read &lt;code&gt;kept&lt;/code&gt;, with a misleading hint about if/otherwise branches; the buffer declaration itself raised no error at all. A buffer declaration now runs the same redeclaration check every other typed declaration already gets, and the analyzer no longer reports a read of a name two declarations disagree on as unknown, since the one diagnostic that matters, &amp;quot;&amp;apos;kept&amp;apos; is already declared as a list&amp;quot;, now lands on the second declaration, naming both kinds (#123).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A number, text, or other plain variable declared in every branch of an &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;otherwise&lt;/code&gt; is now visible after it, exactly as a file handle already was.&lt;/strong&gt; A file handle worked because it always registered directly as a global declaration; a plain variable instead only counted toward a branch&amp;apos;s own guard bucket whenever that branch&amp;apos;s condition was a bare boolean, so the check for &amp;quot;declared on every path&amp;quot; never saw it and a later read failed with &lt;code&gt;used before it is declared&lt;/code&gt;, contradicting LANGUAGE.md&amp;apos;s &amp;quot;Declarations in Branches&amp;quot; rule. A branch&amp;apos;s own guarded declarations now also count toward what it definitely declares (#119).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;To&lt;/code&gt;/&lt;code&gt;Library&lt;/code&gt; definition refused for starting inside an open clause now names the specific clause it is still inside, instead of a generic list of every clause kind a definition might ever be nested in.&lt;/strong&gt; The refusal itself dates to #96; the message now says, for example, that &amp;quot;a &lt;code&gt;While&lt;/code&gt; loop is still open here&amp;quot; or &amp;quot;an &lt;code&gt;Otherwise&lt;/code&gt; branch is still open here&amp;quot;, naming whichever &lt;code&gt;If&lt;/code&gt;/&lt;code&gt;Otherwise&lt;/code&gt; branch, loop, or &lt;code&gt;On error&lt;/code&gt; handler actually applies (#122).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A possessive member call now looks past a line break before its preposition, exactly as a free call already did.&lt;/strong&gt; &lt;code&gt;origin&amp;apos;s &amp;apos;scaled&amp;apos;&lt;/code&gt; followed by a line break then &lt;code&gt;of 2&lt;/code&gt; used to refuse with &amp;quot;Expected a statement, got Of&amp;quot;, closing the sentence early even though a single line break is cosmetic and only a period or a paragraph break can end a clause; the same statement on one line, or a free call across the same line break, already compiled. Both possessive-call forms (the instance form and the type form, &lt;code&gt;a &amp;lt;type&amp;gt;&amp;apos;s &amp;apos;member&amp;apos; &amp;lt;prep&amp;gt; &amp;lt;arg&amp;gt;&lt;/code&gt;) now skip a line break before testing for the connector, the same as the free-call path; a paragraph break before the preposition still force-closes the sentence (#120).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A malformed decimal precision in a format slot is now a compile error, never a silent no-op.&lt;/strong&gt; &lt;code&gt;{n:.z}&lt;/code&gt; used to render as a bare &lt;code&gt;{n}&lt;/code&gt;, and a width followed by a &lt;code&gt;.&lt;/code&gt; that named no precision (&lt;code&gt;{n:8.2z}&lt;/code&gt;, &lt;code&gt;{n:8.}&lt;/code&gt;) fell through the same gap: the leading-dot precision reader, and the one for a precision written after a width, both returned without a diagnostic whenever what followed the dot was not all digits. Both now raise the same unrecognised-specifier error #98 already gives an unknown base letter, naming the clause as written and the valid forms (#127).&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;each &amp;lt;name&amp;gt; from &amp;lt;buffer&amp;gt;&lt;/code&gt; walks a buffer&amp;apos;s bytes as numbers, &lt;code&gt;byte&lt;/code&gt; allowed as the loop variable (closes the open half of #104).&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;auto/enable/disable (and their -matic/-d spellings) are no longer reserved words — the unimplemented auto-error-catching paths are removed (owner ruling).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.14</title>
    <id>tag:vox-lang.dev,2026:release/0.4.14</id>
    <updated>2026-08-28T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.14"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A call missing its preposition now names the missing preposition. Plus 5 more changes.</summary>
    <content type="html">&lt;p&gt;Four register fixes (#102, #105, #106, #108) and the diagnostic halves of two more (#103, #104); a &lt;code&gt;Free&lt;/code&gt; statement that releases a buffer&amp;apos;s memory immediately; the reserved-word tables now generated from one source. 33,000 fuzzer programs on 0.4.13 found no further compiler defects.&lt;/p&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A call missing its preposition now names the missing preposition.&lt;/strong&gt; A bare or quoted name written right after a callee with no &lt;code&gt;of&lt;/code&gt;/&lt;code&gt;to&lt;/code&gt;/ &lt;code&gt;with&lt;/code&gt;/&lt;code&gt;on&lt;/code&gt; used to be silently split into two statements — the call reporting the wrong arity and the name a second, unrelated &amp;quot;Unknown function&amp;quot; error. It is now one diagnostic, anchored at the name itself (#105).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A rejected thing field type no longer also fires a garbled default-mismatch error.&lt;/strong&gt; The default-vs-type check ran before the field-type-support check, and had no arm for an unsupported type, so the two slots of &amp;quot;expected X, got Y&amp;quot; rendered the same word. Support is decided first now; an unsupported type reports only the &amp;quot;cannot hold yet&amp;quot; error (#103).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;each ... from&lt;/code&gt; over a non-list anchors its caret at the loop, not the collection&amp;apos;s declaration.&lt;/strong&gt; The diagnostic searched for the collection&amp;apos;s textually-first mention, which in a large file could be hundreds of lines from the offending loop. It now anchors at the &lt;code&gt;each ... from&lt;/code&gt; clause&amp;apos;s own use of the collection (#104).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Set &amp;lt;text&amp;gt; to &amp;quot;&amp;lt;format&amp;gt;&amp;quot;&lt;/code&gt; frees the string it replaces instead of leaking one buffer per evaluation.&lt;/strong&gt; A whole-program gate proves a text variable&amp;apos;s string is never shared before its &lt;code&gt;Set&lt;/code&gt; frees the old one, so the natural accumulate idiom (&lt;code&gt;Set acc to &amp;quot;{acc}x&amp;quot;&lt;/code&gt; in a loop) is no longer quadratic (#108).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;print&lt;/code&gt;&amp;apos;s aliases were reserved unevenly, and the keyword table that decides every other reserved word had drifted from the lexer throughout.&lt;/strong&gt; &lt;code&gt;show&lt;/code&gt;, &lt;code&gt;display&lt;/code&gt;, and &lt;code&gt;prints&lt;/code&gt; are live aliases the lexer folds onto &lt;code&gt;print&lt;/code&gt; and stay reserved; &lt;code&gt;say&lt;/code&gt; and &lt;code&gt;output&lt;/code&gt; are not folded at all and stay ordinary variable names — no shipped program&amp;apos;s behaviour changes. The alias fold now lives in exactly one place, a &lt;code&gt;RESERVED_ALIASES&lt;/code&gt; const in &lt;code&gt;src/lexer/tokens.rs&lt;/code&gt;, which &lt;code&gt;string_is_keyword&lt;/code&gt; reads from and LANGUAGE.md&amp;apos;s Reserved Aliases table (now 85 rows, generated from and checked against the same const) is generated from, so the two tables cannot drift apart again the way they did here (#106; closes #238).&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Free&lt;/code&gt;/&lt;code&gt;Release&lt;/code&gt;/&lt;code&gt;Deallocate &amp;lt;buffer&amp;gt;&lt;/code&gt; releases a buffer&amp;apos;s memory immediately; the buffer is then empty and refuses writes with the error flag (documented under Releasing a Buffer).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.13</title>
    <id>tag:vox-lang.dev,2026:release/0.4.13</id>
    <updated>2026-08-24T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.13"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">The coreasm runtime is modular, and programs pull only what they use. Plus 3 more changes.</summary>
    <content type="html">&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The coreasm runtime is modular, and programs pull only what they use.&lt;/strong&gt; The resource monolith is now four focused modules (fd tracking, buffer lifecycle, line reading, render sink); process control lives in its own &lt;code&gt;proc.asm&lt;/code&gt;; a bare &lt;code&gt;Print&lt;/code&gt; no longer carries the string helpers. Binaries shrink accordingly: hello world drops 832 bytes, &lt;code&gt;file_secure.vox&lt;/code&gt; 3,192 — with byte-identical output.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Codegen speaks in named macros.&lt;/strong&gt; Twenty-two coreasm macros replace ~120 inline instruction sequences (&lt;code&gt;SET_LAST_ERROR&lt;/code&gt;, &lt;code&gt;BUFFER_DATA_ADDR&lt;/code&gt;, &lt;code&gt;BOOL_FROM_RAX&lt;/code&gt;, the &lt;code&gt;INT_IS_&lt;/code&gt; family and friends), and the dead 16-byte &lt;code&gt;BUFFER_DATA_PTR&lt;/code&gt; trap is deleted — the emitted behavior is unchanged, the x86_64 knowledge now lives in one place per idea.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The examples tell only the present truth.&lt;/strong&gt; Six stale comment claims rewritten — two had outlived the features they called proposed, one printed a float caveat its own output disproved.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A freshly built compiler now uses its own coreasm.&lt;/strong&gt; The runtime search prefers the tree next to the executable over an installed &lt;code&gt;/usr/share/vox/coreasm&lt;/code&gt;, so a development build can no longer silently assemble against an older release&amp;apos;s runtime (#233).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.12</title>
    <id>tag:vox-lang.dev,2026:release/0.4.12</id>
    <updated>2026-08-24T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.12"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Generated assembly identifies its maker. Plus 5 more changes.</summary>
    <content type="html">&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Generated assembly identifies its maker.&lt;/strong&gt; The banner comment at the top of every emitted &lt;code&gt;.asm&lt;/code&gt; file now reads &lt;code&gt;; Generated by vox &amp;lt;version&amp;gt;&lt;/code&gt; instead of the historical &lt;code&gt;; Generated by ec&lt;/code&gt;.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The packaging spec builds vendored on Amazon Linux.&lt;/strong&gt; AL2023 defines &lt;code&gt;%{fedora}&lt;/code&gt; yet ships neither &lt;code&gt;cargo-rpm-macros&lt;/code&gt; nor the packaged rust crates, so the unbundled path&amp;apos;s condition becomes &lt;code&gt;%if 0%{?fedora} &amp;amp;&amp;amp; !0%{?amzn}&lt;/code&gt;; Fedora chroots are unaffected.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The manual names the words that open a consuming clause.&lt;/strong&gt; Basics gains the list (&lt;code&gt;If&lt;/code&gt;/&lt;code&gt;Otherwise&lt;/code&gt;, &lt;code&gt;While&lt;/code&gt;, &lt;code&gt;For each&lt;/code&gt;, &lt;code&gt;Repeat&lt;/code&gt;, &lt;code&gt;On error&lt;/code&gt;, &lt;code&gt;but if&lt;/code&gt;) and documents period-stacking to close more than one open clause at once.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The &lt;code&gt;examples/&lt;/code&gt; set is curated for the site.&lt;/strong&gt; Six dev-scratch files (&lt;code&gt;test_simple.vox&lt;/code&gt;, &lt;code&gt;exit_test.vox&lt;/code&gt;, &lt;code&gt;func_test.vox&lt;/code&gt;, &lt;code&gt;file_test.vox&lt;/code&gt;, &lt;code&gt;file_simple.vox&lt;/code&gt;, &lt;code&gt;count.vox&lt;/code&gt;) that were never showcase material are removed, and &lt;code&gt;loop_expansion_test.vox&lt;/code&gt; becomes &lt;code&gt;expansion.vox&lt;/code&gt;, a real demonstration of a single &lt;code&gt;each&lt;/code&gt; expansion, a chained two-&lt;code&gt;each&lt;/code&gt; grid, and a &lt;code&gt;but if&lt;/code&gt; branch inside an expansion.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A bare or relative &lt;code&gt;.lib&lt;/code&gt; name in &lt;code&gt;see&lt;/code&gt; now resolves against an installed library&amp;apos;s interface.&lt;/strong&gt; The search order for a &lt;code&gt;.lib&lt;/code&gt; gains &lt;code&gt;/usr/include/vox&lt;/code&gt; as its final step — after the containing file&amp;apos;s directory and every &lt;code&gt;--lib-path&lt;/code&gt; — so &lt;code&gt;see json version &amp;quot;0.1&amp;quot; from &amp;quot;json.lib&amp;quot;.&lt;/code&gt; finds an installed library with no flag needed, while a development &lt;code&gt;.lib&lt;/code&gt; beside the source or on &lt;code&gt;--lib-path&lt;/code&gt; still shadows the installed one of the same name. The &amp;quot;Paths tried&amp;quot; diagnostic on a miss now names the system directory too, and the emitted &lt;code&gt;RUNPATH&lt;/code&gt; no longer copies every &lt;code&gt;--lib-path&lt;/code&gt; directory verbatim — only directories a &lt;code&gt;see&lt;/code&gt;d &lt;code&gt;.so&lt;/code&gt; actually resolved from land on it (#99).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A zero-width format specifier, &lt;code&gt;{n:0}&lt;/code&gt; or &lt;code&gt;{n:00000}&lt;/code&gt;, compiled as an unknown-specifier error.&lt;/strong&gt; A width of 0 pads nothing — the manual&amp;apos;s width row carries no floor on &lt;code&gt;N&lt;/code&gt;, and 0.4.10 already treated it as a no-op — but the #98 fix that refused an unrecognised clause caught a clause of nothing but zeros along with it, since stripping every leading zero for the width left no digits behind either way. A bare &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;00...0&lt;/code&gt; is now read as its own case, a width of zero, rather than falling through to the catch-all (#100).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.11</title>
    <id>tag:vox-lang.dev,2026:release/0.4.11</id>
    <updated>2026-08-23T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.11"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">LANGUAGE.md carries no em dashes. Plus 12 more changes.</summary>
    <content type="html">&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;LANGUAGE.md carries no em dashes.&lt;/strong&gt; The house rule for public copy now applies to the specification: every em dash is rewritten as ordinary punctuation, no sentence changed, and one heading reads “Chained &lt;code&gt;each&lt;/code&gt; clauses: a grid”.&lt;/li&gt;&lt;li&gt;A &lt;code&gt;To&lt;/code&gt; (function definition) or &lt;code&gt;Library&lt;/code&gt; declaration reached while an &lt;code&gt;If&lt;/code&gt;, a loop, or another definition&amp;apos;s body is still open is now a compile error naming the construct and saying to move it above the block, instead of being silently parsed into that body and shifting the control flow of every statement written after it (#96).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Packaging: &lt;code&gt;vox.spec&lt;/code&gt; passes &lt;code&gt;fedora-review&lt;/code&gt;.&lt;/strong&gt; The spec now builds two ways from one file: unbundled on Fedora, against the packaged &lt;code&gt;rust-*-devel&lt;/code&gt; crates with the cargo RPM macros, and vendored everywhere else (EPEL, CentOS Stream, openSUSE, Mageia, Amazon Linux, openEuler, Azure Linux, ELN) with every bundled crate declared. It gained a &lt;code&gt;%check&lt;/code&gt; that runs the Rust test suite, &lt;code&gt;BuildRequires: gcc&lt;/code&gt;, the effective licence of the binary (&lt;code&gt;GPL-3.0-or-later AND (MIT OR Apache-2.0)&lt;/code&gt;), and a changelog entry for the version it builds; &lt;code&gt;.copr/Makefile&lt;/code&gt; now ships the exact upstream GitHub archive as &lt;code&gt;Source0&lt;/code&gt; for a release build, so its checksum matches the URL in the spec.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;man vox&lt;/code&gt;.&lt;/strong&gt; A manual page covering every flag, the six-step &lt;code&gt;coreasm&lt;/code&gt; resolution order, the files the compiler reads and writes, and four worked examples. Installed by &lt;code&gt;make install&lt;/code&gt;, by the RPM, and by the Nix flake.&lt;/li&gt;&lt;li&gt;VS Code extension 0.4.2: README links the website (vox-lang.dev, /docs/), &lt;code&gt;homepage&lt;/code&gt; set in package.json, the README&amp;apos;s Visual Studio Marketplace link fixed to &lt;code&gt;vox-lang.vox-lang&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;VS Code extension 0.4.1&lt;/strong&gt;: the grammar now scopes list/map literal punctuation — &lt;code&gt;[&lt;/code&gt; &lt;code&gt;]&lt;/code&gt; as &lt;code&gt;punctuation.section.brackets.begin/end.vox&lt;/code&gt;, &lt;code&gt;{&lt;/code&gt; &lt;code&gt;}&lt;/code&gt; as &lt;code&gt;punctuation.section.braces.begin/end.vox&lt;/code&gt;, the map key/value &lt;code&gt;:&lt;/code&gt; as &lt;code&gt;punctuation.separator.key-value.vox&lt;/code&gt;, and &lt;code&gt;,&lt;/code&gt; as &lt;code&gt;punctuation.separator.comma.vox&lt;/code&gt;. Previously these characters carried no scope at all inside a list or map literal (&lt;code&gt;a list called xs is [1, &amp;quot;two&amp;quot;, nothing].&lt;/code&gt;) while every value between them was already highlighted.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;buffer&lt;/code&gt; global written with &lt;code&gt;Set&lt;/code&gt; at top level is still a global.&lt;/strong&gt; &lt;code&gt;Set roster to [&amp;quot;ada&amp;quot;].&lt;/code&gt; after &lt;code&gt;a list called roster is [].&lt;/code&gt; took the name out of scope entirely, so every function reading it was refused with &lt;code&gt;Unknown variable&lt;/code&gt;, while the byte-equivalent &lt;code&gt;the roster is [&amp;quot;ada&amp;quot;].&lt;/code&gt; compiled and ran; a write that names no type now claims no kind. The &lt;code&gt;Unknown variable&lt;/code&gt; caret for a possessive read also lands on the read that failed rather than on the declaration (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#92&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;The diagnostic for reading the result of a &lt;code&gt;.lib&lt;/code&gt; entry with no &lt;code&gt;, returning&lt;/code&gt; clause cited LANGUAGE.md by line number; both citations had drifted onto unrelated text. It now cites the two sections by name, &lt;code&gt;(LANGUAGE.md &amp;quot;The .lib file&amp;quot;)&lt;/code&gt; and &lt;code&gt;(LANGUAGE.md &amp;quot;Consuming a library&amp;quot;)&lt;/code&gt;, matching the rest of the compiler&amp;apos;s diagnostics (#93).&lt;/li&gt;&lt;li&gt;LANGUAGE.md and the compiler&amp;apos;s uninitialized-buffer warning said a fresh dynamic buffer starts with zero capacity; the runtime has always given it 4096 bytes up front. LANGUAGE.md and the warning now say so (#93).&lt;/li&gt;&lt;li&gt;&lt;code&gt;Set message to &amp;quot;x&amp;quot;.&lt;/code&gt; named &lt;code&gt;to&lt;/code&gt; as the reserved keyword instead of &lt;code&gt;message&lt;/code&gt;, and &lt;code&gt;The message is &amp;quot;x&amp;quot;.&lt;/code&gt; raised an unrelated internal-name error — both blamed whatever token happened to follow a reserved type noun used without &lt;code&gt;called&lt;/code&gt;, instead of the type noun itself. Both statement forms (and the equivalent &lt;code&gt;a message is &amp;quot;x&amp;quot;.&lt;/code&gt;) now name the reserved word the author actually typed, matching the diagnostic the &lt;code&gt;a &amp;lt;type&amp;gt; called &amp;lt;name&amp;gt;&lt;/code&gt; declaration path already gave (#94).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A name brought into being without a type noun is now locked to that type, and reads back as it&lt;/strong&gt; — &lt;code&gt;Set zoo to 5.&lt;/code&gt; followed by &lt;code&gt;Set zoo to &amp;quot;text now&amp;quot;.&lt;/code&gt; compiled clean and printed &lt;code&gt;4198488&lt;/code&gt;, the string&amp;apos;s own address, instead of the compile error &lt;code&gt;a number called zoo is 5.&lt;/code&gt; gives for the same write; and &lt;code&gt;Set label to &amp;quot;hello&amp;quot;. Print label.&lt;/code&gt; printed an address on the first read, no rewrite involved, as did a map. &lt;code&gt;Set NAME to VALUE.&lt;/code&gt;, &lt;code&gt;the NAME is VALUE.&lt;/code&gt; and &lt;code&gt;NAME is VALUE.&lt;/code&gt; on a name that does not exist yet each declare it with the value&amp;apos;s type, which is then fixed like any other declaration&amp;apos;s — so &lt;code&gt;NAME&amp;apos;s type&lt;/code&gt; names it and says &lt;code&gt;(static)&lt;/code&gt;, and a later write of another type is refused with the caret on the write and the note on the declaration. One untyped &lt;code&gt;Set&lt;/code&gt; anywhere in a file used to switch the type lock off for that name in every spelling, and to make a read placed between an earlier write and that &lt;code&gt;Set&lt;/code&gt; fail as &amp;quot;used before it is declared&amp;quot; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#95&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A list handed to a function that appends to it stayed on the caller&amp;apos;s homogeneous fast path&lt;/strong&gt;, so an element the callee stored reads back as its address: &lt;code&gt;To &amp;apos;note whatever&amp;apos; with a list called noted and a text called label. append label to noted.&lt;/code&gt;, called on an empty &lt;code&gt;noted&lt;/code&gt;, made &lt;code&gt;Print &amp;quot;{noted&amp;apos;s last}&amp;quot;&lt;/code&gt; print &lt;code&gt;4198536&lt;/code&gt; where the same append written at the caller printed &lt;code&gt;tail&lt;/code&gt;. The heterogeneous pre-scan decided each function&amp;apos;s lists in isolation, so a write through a &lt;code&gt;list&lt;/code&gt; parameter never reached the list&amp;apos;s owner. It now carries across the call: a list is widened to mixed at a call site unless the callee provably writes the one type the caller has already proven the list holds, so &lt;code&gt;&amp;apos;s first&lt;/code&gt;, &lt;code&gt;&amp;apos;s last&lt;/code&gt;, &lt;code&gt;element N of&lt;/code&gt;, iteration and a &lt;code&gt;{...}&lt;/code&gt; hole all read the slot&amp;apos;s own runtime tag. A list only read by the function it is handed to, or given the type it already holds, keeps the fast path. Across a &lt;code&gt;.lib&lt;/code&gt; boundary the widening is unconditional — a signature cannot say what a body writes — which also ends a segfault there: a library declaring &lt;code&gt;a list of text&lt;/code&gt; was believed by a caller whose list held numbers, and the first read dereferenced the integer &lt;code&gt;1&lt;/code&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#97&lt;/a&gt;).&lt;/li&gt;&lt;li&gt;&lt;strong&gt;An unrecognised format specifier compiled clean and quietly rendered as a bare &lt;code&gt;{name}&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;{n:q}&lt;/code&gt;, &lt;code&gt;{n:#x}&lt;/code&gt; and &lt;code&gt;{n:zzz}&lt;/code&gt; all printed &lt;code&gt;n&lt;/code&gt;&amp;apos;s plain value with no warning, so a typo like &lt;code&gt;#x&lt;/code&gt; for hex silently gave the wrong output. Any specifier that is not a width, a zero-padded width, a decimal precision, or one of the base letters is now a compile error naming what was written and the valid forms (#98).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.10</title>
    <id>tag:vox-lang.dev,2026:release/0.4.10</id>
    <updated>2026-08-22T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.10"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Libraries built by an older Vox must be rebuilt. Plus 26 more changes.</summary>
    <content type="html">&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Libraries built by an older Vox must be rebuilt.&lt;/strong&gt; A &lt;code&gt;.so&lt;/code&gt; exporting a function with a &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;buffer&lt;/code&gt; parameter, and every program that &lt;code&gt;see&lt;/code&gt;s it, must be built by the same 0.4.10 compiler: those parameters now carry the address of the caller&amp;apos;s storage, so a collection grown inside the function reaches the caller instead of being lost (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#75&lt;/a&gt;, &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#90&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A global declared below a function is read inside that function as a raw machine word&lt;/strong&gt; — &lt;code&gt;Print label.&lt;/code&gt; inside a function defined above &lt;code&gt;a text called label is &amp;quot;hello&amp;quot;.&lt;/code&gt; printed &lt;code&gt;4198488&lt;/code&gt;, the string&amp;apos;s own address, while a &lt;code&gt;float&lt;/code&gt; printed its IEEE-754 bits and a &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;buffer&lt;/code&gt; a live heap address; only a &lt;code&gt;number&lt;/code&gt; came back right, and no diagnostic was raised either way. Codegen now reads every top-level declaration&amp;apos;s type in a pre-pass and gives it to each function body before generating it, so a global is read as its declared type wherever the declaration sits — flags included, which #32 had made order-independent only inside the analyzer (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#66&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A declared &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;buffer&lt;/code&gt; return printed directly is now rendered by its own formatter, not the integer one&lt;/strong&gt; — &lt;code&gt;To &amp;apos;give float&amp;apos;. Return a float, 2.5.&lt;/code&gt; then &lt;code&gt;Print &amp;apos;give float&amp;apos;.&lt;/code&gt; printed &lt;code&gt;4612811918334230528&lt;/code&gt;, the bit pattern of 2.5; a declared &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;buffer&lt;/code&gt; return printed a heap address, and a map did so in every position, including through a &lt;code&gt;.lib&lt;/code&gt; import. Declaring the return type is the first way out bug #45&amp;apos;s diagnostic offers, so it now works for all eleven types (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#67&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;**A mixed list&amp;apos;s element in the *expression* form of a format hole no longer prints as an integer** — &lt;code&gt;Print &amp;quot;{element 2 of nested}&amp;quot;&lt;/code&gt; printed a live heap address that changed between runs, while &lt;code&gt;Print element 2 of nested.&lt;/code&gt; printed &lt;code&gt;[2, 3]&lt;/code&gt; on the same element in the same run; a text element printed a &lt;code&gt;.rodata&lt;/code&gt; address and a decimal element its raw IEEE-754 bits. The tag was loaded and then discarded: the hole&amp;apos;s expression path rendered by the compiler&amp;apos;s static guess instead of the slot&amp;apos;s runtime tag. It now dispatches on that tag in every sink — Print, a text initializer, buffer &lt;code&gt;set&lt;/code&gt;/&lt;code&gt;copy&lt;/code&gt;/&lt;code&gt;append&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, filesystem paths, &lt;code&gt;treating&lt;/code&gt; clauses and function arguments — through the same renderers Print calls, so a &lt;code&gt;value&lt;/code&gt; interpolated outside Print is right too. The two LANGUAGE.md sentences that documented the limitation are gone (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#68&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;treating&lt;/code&gt; clause whose match or replacement is a &lt;code&gt;value&lt;/code&gt; now reads its runtime tag&lt;/strong&gt; — &lt;code&gt;print each item from [1, &amp;quot;-&amp;quot;] treating probe as &amp;quot;X&amp;quot;.&lt;/code&gt;, with &lt;code&gt;a value called probe is &amp;quot;-&amp;quot;.&lt;/code&gt;, printed &lt;code&gt;1&lt;/code&gt; then a rodata address, and over a text list a &lt;code&gt;value&lt;/code&gt; holding a number went into &lt;code&gt;_str_eq&lt;/code&gt; as a &lt;code&gt;char*&lt;/code&gt; and segfaulted. A &lt;code&gt;value&lt;/code&gt; carries no tag at emit time, so the clause fell back to the static path #59 replaced; the tag test and the choice between comparing bytes and comparing registers are now runtime branches, and a fired substitution carries the replacement&amp;apos;s own tag out with it (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#69&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;append each &amp;lt;var&amp;gt; from &amp;lt;collection&amp;gt; treating &amp;lt;match&amp;gt; as &amp;lt;replacement&amp;gt; to &amp;lt;list&amp;gt;&lt;/code&gt; now substitutes instead of dropping the clause&lt;/strong&gt; — the clause was parsed and thrown away, so &lt;code&gt;append each name from names treating &amp;quot;-&amp;quot; as &amp;quot;anon&amp;quot; to out.&lt;/code&gt; appended &lt;code&gt;[&amp;quot;ann&amp;quot;, &amp;quot;-&amp;quot;]&lt;/code&gt;, and over a range source the same sentence would not parse at all. Written after the destination it now names itself instead of falling through as &lt;code&gt;Expected a statement, got Treating&lt;/code&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#70&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A format specifier other than a width now honours the value&amp;apos;s type&lt;/strong&gt; — &lt;code&gt;{n:.2}&lt;/code&gt; on a whole number read the integer&amp;apos;s bits as a double and printed &lt;code&gt;0.00&lt;/code&gt;; &lt;code&gt;{t:x}&lt;/code&gt; on a &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;{b:x}&lt;/code&gt; on a &lt;code&gt;buffer&lt;/code&gt; printed the variable&amp;apos;s ADDRESS, so two texts holding the same bytes printed two different numbers and a buffer printed a live heap pointer that moved between runs. v0.4.7 fixed this for the width specifier only (#36); the precision and radix paths never consulted the type at all. A precision on a whole number now prints it to that many places (&lt;code&gt;255.00&lt;/code&gt;, exactly, for every number Vox can hold), and a specifier the type cannot answer — a radix on a &lt;code&gt;text&lt;/code&gt;, a &lt;code&gt;buffer&lt;/code&gt; or a &lt;code&gt;float&lt;/code&gt;, a precision on a &lt;code&gt;text&lt;/code&gt; or a &lt;code&gt;buffer&lt;/code&gt; — is a compile error naming the cast, in #45/#62/#63/#65&amp;apos;s family (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#71&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;An absent map key, or an out-of-range list index, is no longer typed from the collection&amp;apos;s values&lt;/strong&gt; — &lt;code&gt;a map called guess is {&amp;quot;a&amp;quot;: &amp;quot;t&amp;quot;}.&lt;/code&gt; followed by the manual&amp;apos;s own idiom, &lt;code&gt;a number called n is guess&amp;apos;s &amp;quot;absent&amp;quot;.&lt;/code&gt;, was refused on 0.4.9 with &amp;quot;cannot initialise &amp;apos;n&amp;apos;, which is a number, with a text read out of map &amp;apos;guess&amp;apos;&amp;quot;; 0.4.8 compiled it and printed &lt;code&gt;0&lt;/code&gt;. A key the literal does not contain, and an index past a list literal&amp;apos;s end, yield the &lt;strong&gt;number&lt;/strong&gt; 0 (LANGUAGE.md:2429, :2857), so the read is now typed &lt;code&gt;number&lt;/code&gt; whatever the collection holds. The &lt;code&gt;text&lt;/code&gt; spelling that 0.4.9&amp;apos;s own &lt;code&gt;help:&lt;/code&gt; line recommended dereferenced that 0 as a pointer and segfaulted; it is now a compile error naming the absent key. The proof is withheld — and the behaviour left exactly as it was — for any collection an &lt;code&gt;Append&lt;/code&gt;, a &lt;code&gt;Set&lt;/code&gt;, an alias or a call can reach (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#72&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A function definition swallowed into an open clause is now called with the right ABI&lt;/strong&gt; — a &lt;code&gt;To&lt;/code&gt; written while a &lt;code&gt;For each&lt;/code&gt;, &lt;code&gt;While&lt;/code&gt;, &lt;code&gt;Repeat&lt;/code&gt;, &lt;code&gt;If&lt;/code&gt; or &lt;code&gt;on error&lt;/code&gt; was still open is parsed into that clause&amp;apos;s body, and the pre-passes that record each function&amp;apos;s signature scanned only the top-level statement list, so every call to such a definition was compiled against an empty signature: a &lt;code&gt;value&lt;/code&gt; parameter&amp;apos;s tag word was never pushed and the callee read its tag from a register the caller never wrote, which turned an integer argument into a text pointer and segfaulted in eleven lines. The analyzer and codegen now find nested definitions through one shared sweep, so a call is compiled the same wherever the definition stands (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#73&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;&amp;apos;s &amp;lt;property&amp;gt;&lt;/code&gt; read is type-checked where it lands.&lt;/strong&gt; &lt;code&gt;a text called t is xs&amp;apos;s length.&lt;/code&gt; compiled and segfaulted on the first read, and so did the same value passed to a text parameter, returned from a text function, or assigned with &lt;code&gt;Set&lt;/code&gt; — the type lock&amp;apos;s oracle answered for &lt;code&gt;first&lt;/code&gt; and &lt;code&gt;last&lt;/code&gt; and treated every other property as &amp;quot;type unknown&amp;quot;. Every property whose type is the same whatever it is read from now proves that type, so a mismatch is a compile error naming the two ways out; &lt;code&gt;first&lt;/code&gt;, &lt;code&gt;last&lt;/code&gt;, &lt;code&gt;absolute&lt;/code&gt;, &lt;code&gt;duration&lt;/code&gt; and &lt;code&gt;elapsed&lt;/code&gt; are typed by what they are read from and stay unchecked (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#74&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A list or map grown through a function parameter reaches the caller, instead of stopping at the size its literal happened to allocate&lt;/strong&gt; — &lt;code&gt;append&lt;/code&gt;ing to a &lt;code&gt;list&lt;/code&gt; parameter (or inserting into a &lt;code&gt;map&lt;/code&gt; parameter) silently stopped after &lt;code&gt;max(8, element count)&lt;/code&gt; elements: &lt;code&gt;_list_append&lt;/code&gt;/&lt;code&gt;_map_insert&lt;/code&gt; returned the reallocated pointer and the callee stored it only into its own parameter slot, so the caller kept pointing at the block the collection outgrew. Every call past that reallocated afresh and dropped the new block, leaking a whole collection per call — 781 MiB of resident memory for a list that reported eight elements. A &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt; argument now travels as the address of the caller&amp;apos;s storage, the shape a &lt;code&gt;thing&lt;/code&gt; argument already used, and the store-back writes the new pointer through it; growth arrives however many calls deep the collection was passed. &lt;strong&gt;A &lt;code&gt;.so&lt;/code&gt; exporting a function with a &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt; parameter, and the programs that &lt;code&gt;see&lt;/code&gt; it, must be rebuilt together: their calling convention changed&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#75&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;map&lt;/code&gt; parameter is no longer typed as nothing at all&lt;/strong&gt; — &lt;code&gt;To &amp;apos;show&amp;apos; with a map called holder. Print holder&amp;apos;s length.&lt;/code&gt; did not fail at runtime, it failed to &lt;strong&gt;assemble&lt;/strong&gt;: &lt;code&gt;holder&amp;apos;s length&lt;/code&gt; emitted a call to &lt;code&gt;_file_size&lt;/code&gt;, and NASM reported an undefined symbol that appears nowhere in the program, with no Vox diagnostic. The same parameter printed a raw heap address from &lt;code&gt;Print holder&lt;/code&gt; and &lt;code&gt;&amp;quot;{holder}&amp;quot;&lt;/code&gt;, and answered &lt;code&gt;-1&lt;/code&gt; for &lt;code&gt;&amp;apos;s length&lt;/code&gt; in the programs that did happen to link the file runtime. The declared-type to codegen-type table had been copied out four times and &lt;code&gt;map&lt;/code&gt; had reached only the declaration&amp;apos;s copy, so a map parameter — and a declared &lt;code&gt;map&lt;/code&gt; return, whose result leaked an address the same way — fell to &lt;code&gt;Unknown&lt;/code&gt;, which every property and print dispatch reads as the file branch. The four copies are now one &lt;code&gt;vartype_of_declared_type&lt;/code&gt;. Found by the vox-fuzz candidate audit and adjudicated by the language lawyer (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#76&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;The &lt;code&gt;append&lt;/code&gt; value slot now reads the values every other value position reads: a negative literal, &lt;code&gt;nothing&lt;/code&gt;, &lt;code&gt;element N of &amp;lt;list&amp;gt;&lt;/code&gt;, &lt;code&gt;byte N of &amp;lt;buffer&amp;gt;&lt;/code&gt;, a &lt;code&gt;&amp;apos;s&lt;/code&gt; possessive and the operator spelling &lt;code&gt;times&lt;/code&gt; were refused there and accepted everywhere else — including inside &lt;code&gt;{...}&lt;/code&gt; in the slot itself. The value was parsed by two hand-written copies of the general parser that had fallen behind it; it reads one primary now, with &lt;code&gt;to&lt;/code&gt; still reserved as the append separator (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#77&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A buffer sized from a variable no longer escapes the size bound&lt;/strong&gt; — &lt;code&gt;a number called wanted is 0 minus 1.&lt;/code&gt; followed by &lt;code&gt;a buffer called b is wanted bytes in size.&lt;/code&gt; compiled and reported &lt;code&gt;capacity -1&lt;/code&gt;, and a size past what the system can map (from a named number, a float read as its bit pattern, or an argument) built a null buffer that segfaulted on the first read. The 1..1073741824-byte bound lived in the parser&amp;apos;s literal arm alone, so it was a rule about a spelling: &lt;code&gt;Create a buffer called b with capacity 1073741825.&lt;/code&gt; was never bounded at all. The bound is now held wherever a size is decided — at compile time for a size the compiler can prove, and at run time for one it cannot, where the buffer is made with no capacity and the error flag is raised for &lt;code&gt;On error&lt;/code&gt; to catch. LANGUAGE.md now states the bound (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#78&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A top-level read written above the variable&amp;apos;s own declaration is now a compile error&lt;/strong&gt; — &lt;code&gt;Print label.&lt;/code&gt; above &lt;code&gt;a text called label is &amp;quot;hello&amp;quot;.&lt;/code&gt; used to compile clean and print &lt;code&gt;0&lt;/code&gt;: the name resolved from a whole-program pre-pass while its type came from the in-order walk and was not there yet, so codegen formatted the zeroed &lt;code&gt;.bss&lt;/code&gt; slot as an integer. The collection, map and buffer spellings of the same too-early read segfaulted instead — &lt;code&gt;append&lt;/code&gt;, &lt;code&gt;element N of&lt;/code&gt;, a map key read, &lt;code&gt;Clear&lt;/code&gt;, &lt;code&gt;Resize&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt; and &lt;code&gt;For each&lt;/code&gt; among them. The top-level walk now fills its scope in declaration order, and the read is answered with a diagnostic naming the line the declaration is on. A function body is unaffected: it runs when it is called, so it may still name a global declared further down the file (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#79&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;thing&lt;/code&gt; instance declared below a function can now be read inside it&lt;/strong&gt; — &lt;code&gt;Print origin&amp;apos;s x.&lt;/code&gt; inside a function was a parse error, &lt;code&gt;Expected property name, got Identifier(&amp;quot;x&amp;quot;)&lt;/code&gt;, whenever &lt;code&gt;a point called origin.&lt;/code&gt; was written below that function; the caret landed on the field name, the one token in the line that was not the problem. The write, the article spelling, the format hole, a chain through a nested thing and the instance possessive all failed the same way. LANGUAGE.md attaches no ordering condition to a top-level variable, and the rule it does state is about a thing *definition*, which the repro already obeyed. The parser now registers every top-level &lt;code&gt;a &amp;lt;thing&amp;gt; called &amp;lt;name&amp;gt;&lt;/code&gt; declaration in one pass before the first statement is parsed — the whole-program answer the analyzer and codegen have always had. A thing used above its *definition* is still refused, and now says so instead of complaining about the field name (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#80&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A quoted map key inside a format hole is read as a key, not as the end of the hole&lt;/strong&gt; — &lt;code&gt;Print &amp;quot;[{scores&amp;apos;s \&amp;quot;{key}\&amp;quot;}]&amp;quot;.&lt;/code&gt; printed &lt;code&gt;[1&amp;quot;}]&lt;/code&gt;, rendering the right value and then spilling the hole&amp;apos;s own &lt;code&gt;&amp;quot;&lt;/code&gt; and &lt;code&gt;}&lt;/code&gt; into the output; a key containing &lt;code&gt;}&lt;/code&gt; or &lt;code&gt;:&lt;/code&gt; was cut in half and quietly answered the wrong value. The hole parser scanned to the first &lt;code&gt;}&lt;/code&gt; and split the format spec at the first &lt;code&gt;:&lt;/code&gt; without noticing a quoted string in between. Both now step over quoted strings, so a dynamic key composes with a format hole as both features are documented (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#81&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A float read from text is now the same double as the same number written as a literal&lt;/strong&gt; — &lt;code&gt;&amp;quot;0.88&amp;quot; as a float&lt;/code&gt; was not &lt;code&gt;0.88&lt;/code&gt;, and 53 of the 1000 two-decimal values below ten disagreed with their own literal, because the runtime parser divided the fractional part by ten once per digit and every one of those divisions rounded. The whole decimal is now read as one mantissa and the point placed in a single rounding, so every decimal of up to 18 significant digits converts to exactly the double the compiler&amp;apos;s own parser gives it. The same rewrite stops a long decimal wrapping the parser&amp;apos;s accumulator — &lt;code&gt;&amp;quot;3.141592653589793238462643&amp;quot;&lt;/code&gt; used to read as &lt;code&gt;2.999995446079999&lt;/code&gt; and &lt;code&gt;&amp;quot;123456789012345678901.5&amp;quot;&lt;/code&gt; as a negative number — and stops an empty buffer cast to a float handing back whatever float was computed last. Found by the vox-fuzz claim ledger row VAL-09 and adjudicated by the language lawyer (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#82&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;not&lt;/code&gt; now takes the whole condition after it, so &lt;code&gt;If not v1 is v2 then,&lt;/code&gt; is &lt;code&gt;not (v1 is v2)&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;not&lt;/code&gt; was parsed as an operator on a primary, so that guard compiled as &lt;code&gt;(not v1) is v2&lt;/code&gt; and was false whatever the operands, and &lt;code&gt;not &amp;lt;comparison&amp;gt;&lt;/code&gt; had no working spelling at all. A &lt;code&gt;not&lt;/code&gt; level now sits between &lt;code&gt;and&lt;/code&gt; and the comparisons, matching the &lt;code&gt;not &amp;lt;condition&amp;gt;&lt;/code&gt; the manual documents, and &lt;code&gt;is not&lt;/code&gt;, &lt;code&gt;not&lt;/code&gt; in front of a boolean, and &lt;code&gt;not&lt;/code&gt; in a printed value are unchanged (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#83&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;isn&amp;apos;t&lt;/code&gt; and &lt;code&gt;aren&amp;apos;t&lt;/code&gt; now compile&lt;/strong&gt; — both have always been listed in LANGUAGE.md&amp;apos;s Logical Operators table as spellings of &lt;code&gt;not&lt;/code&gt;, but the lexer stopped reading a word at the apostrophe, so neither could ever be built and any program that used one was refused with &lt;code&gt;Expected a statement, got Apostrophe&lt;/code&gt;. Each now lexes as the two words it stands for — &lt;code&gt;is not&lt;/code&gt; and &lt;code&gt;are not&lt;/code&gt; — while the four undocumented contractions that sat unreachable beside them in the same table (&lt;code&gt;doesn&amp;apos;t&lt;/code&gt;, &lt;code&gt;don&amp;apos;t&lt;/code&gt;, &lt;code&gt;it&amp;apos;s&lt;/code&gt;, &lt;code&gt;they&amp;apos;re&lt;/code&gt;) were removed rather than woken, so &lt;code&gt;print it&amp;apos;s length.&lt;/code&gt; stays the possessive on a variable called &lt;code&gt;it&lt;/code&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#84&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A width and a precision written together in one format specifier now compose instead of dropping both&lt;/strong&gt; — &lt;code&gt;{f:8.2}&lt;/code&gt; printed &lt;code&gt;2.5&lt;/code&gt; though &lt;code&gt;{f:.2}&lt;/code&gt; prints &lt;code&gt;2.50&lt;/code&gt;, because the spec reader consumed the width and then matched the leftover &lt;code&gt;.2&lt;/code&gt; against the base specifiers, where it fell to a catch-all and the precision was never assigned. Both halves are now read and both are kept: the precision decides the digits and the width decides the padding, each honoured wherever a primitive for it exists — the rule the width already followed on its own. &lt;code&gt;{n:8.2}&lt;/code&gt; on a whole number prints &lt;code&gt;  255.00&lt;/code&gt; and &lt;code&gt;{n:08.2}&lt;/code&gt; prints &lt;code&gt;00255.00&lt;/code&gt;; on a &lt;code&gt;float&lt;/code&gt; the places are printed and the padding is not, because there is no float padder yet (#36&amp;apos;s residue), exactly as a bare &lt;code&gt;{f:8}&lt;/code&gt; already behaved. LANGUAGE.md, which never said whether &lt;code&gt;N.M&lt;/code&gt; composes, now says (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#85&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A float&amp;apos;s precision now renders in every sink, not just &lt;code&gt;Print&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;{ratio:.2}&lt;/code&gt; printed &lt;code&gt;2.50&lt;/code&gt; to the terminal and &lt;code&gt;2.5&lt;/code&gt; everywhere else: a text initializer, buffer &lt;code&gt;set&lt;/code&gt;/&lt;code&gt;copy&lt;/code&gt;/&lt;code&gt;append&lt;/code&gt;, a &lt;code&gt;write&lt;/code&gt; to a file and a function argument, so a receipt written to disk disagreed with the same line shown on screen. Those sinks now render the precision through the same routine &lt;code&gt;Print&lt;/code&gt; uses, which LANGUAGE.md &amp;quot;Format Strings Everywhere&amp;quot; has always promised; every other specifier already agreed and is unchanged (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#86&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;A buffer stored in a &lt;code&gt;value&lt;/code&gt; now carries its bytes instead of its struct pointer: &lt;code&gt;a value called carried is made.&lt;/code&gt; printed an empty line (or the capacity byte — &lt;code&gt;@&lt;/code&gt; for a 64-byte buffer) while &lt;code&gt;carried&amp;apos;s type&lt;/code&gt; reported &lt;code&gt;Text (dynamic)&lt;/code&gt;. The same copy &lt;code&gt;as text&lt;/code&gt; makes is now made at all five &lt;code&gt;value&lt;/code&gt; write sites — declaration, &lt;code&gt;Set&lt;/code&gt;, &lt;code&gt;the ... is&lt;/code&gt;, a &lt;code&gt;value&lt;/code&gt; argument, and &lt;code&gt;Return a value&lt;/code&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#87&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Print not &amp;lt;text&amp;gt;&lt;/code&gt; no longer segfaults&lt;/strong&gt; — a &lt;code&gt;not&lt;/code&gt; is a boolean whatever it is applied to, but &lt;code&gt;infer_expr_type&lt;/code&gt; and &lt;code&gt;is_float_expr&lt;/code&gt; returned its OPERAND&amp;apos;s type, so &lt;code&gt;Print&lt;/code&gt; was told a boolean was text and dereferenced address 0; a list, a map and &lt;code&gt;&amp;quot;{not t}&amp;quot;&lt;/code&gt; in a format string faulted the same way, and a float operand printed &lt;code&gt;0.0&lt;/code&gt; where a boolean belongs. Both predicates now answer boolean for &lt;code&gt;not&lt;/code&gt; and keep propagating the operand&amp;apos;s type for &lt;code&gt;-x&lt;/code&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#88&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The &amp;quot;Unknown variable&amp;quot; caret for a bare literal in a format hole now points at the hole&lt;/strong&gt; — &lt;code&gt;Print &amp;quot;{3.14:.17}&amp;quot;.&lt;/code&gt; is rejected because a hole names a variable, not a literal, but the caret went looking for &lt;code&gt;3.14&lt;/code&gt; as a name and marked the first one anywhere in the file: a legal &lt;code&gt;a float called f is 3.14.&lt;/code&gt; three lines above. The symbol-location scan now asks whether the symbol could have been lexed as a name at all, and for one that could not — a literal the format parser handed back — looks for it inside the text literal it was written in instead of in code. Found by the language lawyer during the round-3 candidate audit (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#89&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;buffer&lt;/code&gt; grown past its capacity through a parameter no longer segfaults the caller&lt;/strong&gt; — &lt;code&gt;To &amp;apos;pad out&amp;apos; with a buffer called sink. append &amp;quot;0123456789&amp;quot; to sink.&lt;/code&gt; called in a loop crashed the caller&amp;apos;s next read of its own buffer, and one &lt;code&gt;Resize sink to 9000&lt;/code&gt; was enough. Growing a buffer moves it and frees the block it grew out of, but the reallocated pointer stopped at the callee&amp;apos;s frame, so the caller was left reading unmapped memory. A &lt;code&gt;buffer&lt;/code&gt; parameter&amp;apos;s argument word is now the address of the cell where the caller keeps its pointer, and the write-back happens at the reallocation rather than when the call returns, so a function called in between that reaches the same buffer sees it where it now lives. A &lt;code&gt;.so&lt;/code&gt; exporting a function with a &lt;code&gt;buffer&lt;/code&gt; parameter must be rebuilt with 0.4.10 alongside its consumer, and a buffer reallocated across that boundary still faults in exit cleanup — recorded in the register as its own defect (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#90&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;An absent-key or out-of-range read into a &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt; no longer segfaults&lt;/strong&gt; — a miss yields the number 0 and sets the error flag, and that 0 used to reach a pointer-typed destination and be dereferenced on the first read wherever the miss was not provable: a variable index, a dynamic key, an &lt;code&gt;Append&lt;/code&gt;-grown list, a &lt;code&gt;Set&lt;/code&gt;-grown map, a collection reached through a parameter. A miss now yields the destination&amp;apos;s default value instead — &lt;code&gt;0&lt;/code&gt; for a &lt;code&gt;number&lt;/code&gt;, the empty text for a &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt; for a &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;{}&lt;/code&gt; for a &lt;code&gt;map&lt;/code&gt; — the same values a declaration with no initializer has written since #25. Where the miss *is* provable, #72&amp;apos;s diagnostic still refuses the program before this rule is reached. The error flag is unchanged, so &lt;code&gt;On error&lt;/code&gt; still catches every one of them (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#91&lt;/a&gt;).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.9</title>
    <id>tag:vox-lang.dev,2026:release/0.4.9</id>
    <updated>2026-08-21T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.9"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">For each over a scalar, a map or a buffer is now a compile error instead of a crash or garbage. Plus 21 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;For each&lt;/code&gt; over a scalar, a map or a buffer is now a compile error instead of a crash or garbage&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#49&lt;/a&gt;) — &lt;code&gt;print each part from 4.&lt;/code&gt;, a two-token program, segfaulted; so did &lt;code&gt;For each part in n,&lt;/code&gt; and &lt;code&gt;append each part from 4 to out.&lt;/code&gt; over a number or a text. A map or a buffer instead iterated silently over nonsense (a 3-entry map ran 3 iterations printing &lt;code&gt;0, 0, 3&lt;/code&gt;; the buffer &lt;code&gt;&amp;quot;abc&amp;quot;&lt;/code&gt; printed &lt;code&gt;6513249&lt;/code&gt;, its own bytes read as a qword). The analyzer checked only that the collection name was defined, and codegen unconditionally read &lt;code&gt;[ptr + 8]&lt;/code&gt; as a list header&amp;apos;s element count — so a number was dereferenced as an address and a map&amp;apos;s or buffer&amp;apos;s own header was misread as a list&amp;apos;s. LANGUAGE.md&amp;apos;s supported collections are a list, a range and &lt;code&gt;arguments&amp;apos;s all&lt;/code&gt;; the analyzer now refuses what it can prove is none of them, suggesting &lt;code&gt;&amp;apos;s keys&lt;/code&gt; or &lt;code&gt;&amp;apos;s values&lt;/code&gt; for a map. It is a known-scalar rejection, not a whitelist: an untyped parameter, a &lt;code&gt;value&lt;/code&gt;, a function result and a property read all keep iterating. Found by the vox-fuzz collections-b claim ledger (discrepancies D3 and D4) and adjudicated by the language lawyer as one memory-safety bug.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A bare &lt;code&gt;otherwise&lt;/code&gt; is accepted after any base action, not just &lt;code&gt;append&lt;/code&gt;&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#50&lt;/a&gt;) — &lt;code&gt;print gauge, but if gauge is greater than 50 print &amp;quot;high&amp;quot;, otherwise print &amp;quot;low&amp;quot;.&lt;/code&gt; was rejected with &lt;code&gt;Expected a statement, got Otherwise&lt;/code&gt;, and &lt;code&gt;increment n, otherwise increment n&lt;/code&gt; failed identically, though LANGUAGE.md documents the bare clause at :393, :399, :2960 and :2966 and says &lt;code&gt;but if&lt;/code&gt; works over any base action. The chain-continuation guard in &lt;code&gt;parse_conditional_suffix&lt;/code&gt; omitted &lt;code&gt;Else&lt;/code&gt; and &lt;code&gt;Otherwise&lt;/code&gt;; only the terse &lt;code&gt;append&lt;/code&gt; branch left a comma behind for the guard to consume, which is why that one spelling worked. The guard now accepts both keywords and no longer consumes them as separators. Found by the vox-fuzz collections-b claim ledger (discrepancy D2) and adjudicated by the language lawyer.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A text-valued special name built into a buffer no longer segfaults&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#52&lt;/a&gt;) — &lt;code&gt;copy &amp;quot;{arguments&amp;apos;s first}&amp;quot; to built&lt;/code&gt; crashed the generated program (exit 139), as did &lt;code&gt;{arguments&amp;apos;s second/last/name/all/raw}&lt;/code&gt; and &lt;code&gt;{environment&amp;apos;s first}&lt;/code&gt;, through all three buffer verbs (&lt;code&gt;set&lt;/code&gt;, &lt;code&gt;copy&lt;/code&gt;, &lt;code&gt;append&lt;/code&gt;). LANGUAGE.md promises every sink shares one name resolver, so these render identically whether printed, written to a file, or built into a buffer — and printing and writing them were always correct. The buffer sink loaded its destination pointer into &lt;code&gt;rdi&lt;/code&gt; before resolving the part&amp;apos;s value, but resolving an argument property passes its index in &lt;code&gt;rdi&lt;/code&gt; to call &lt;code&gt;_get_arg&lt;/code&gt;, so the append that followed dereferenced an index instead of a buffer. The destination is now loaded once the value is settled, which is the order the stack-slot sink beside it already used and why that one never crashed. The numeric specials (&lt;code&gt;{arguments&amp;apos;s count}&lt;/code&gt;, &lt;code&gt;{current time&amp;apos;s hour}&lt;/code&gt;) had been surviving the same path by luck. Regression test &lt;code&gt;tests/bug52_argv_property_into_buffer.vox&lt;/code&gt;, proven to segfault on 0.4.8 and to pass after, plus a codegen test that locks the instruction order. Found by the vox-fuzz Input/Output claim ledger (discrepancy D1).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Return a buffer, &amp;quot;&amp;lt;text&amp;gt;&amp;quot;&lt;/code&gt; is a compile error instead of an empty buffer or a segfault&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#53&lt;/a&gt;) — &lt;code&gt;To &amp;apos;give literal&amp;apos;. Return a buffer, &amp;quot;ABC&amp;quot;.&lt;/code&gt; handed the caller the address of the literal&amp;apos;s characters with no buffer header in front of them, and the caller&amp;apos;s &lt;code&gt;_buffer_append&lt;/code&gt; then read eight bytes past the first character as a length and copied that many bytes. With one string-initialised buffer in the program the call answered a silently &lt;strong&gt;empty&lt;/strong&gt; buffer (&lt;code&gt;size&lt;/code&gt; printed &lt;code&gt;0&lt;/code&gt;); with two it &lt;strong&gt;segfaulted&lt;/strong&gt; (139) — one defect reading different bytes, and which one a program got depended on what the assembler happened to lay down after the literal. LANGUAGE.md:722-727 makes &lt;code&gt;buffer&lt;/code&gt; a legal &lt;code&gt;Return a &amp;lt;type&amp;gt;,&lt;/code&gt; return type and nothing more; the single place the manual gives text a buffer meaning is the declaration initializer &lt;code&gt;a buffer called buf is &amp;quot;Hello&amp;quot;.&lt;/code&gt;. A text literal or a text variable returned as a buffer is now refused with a fix-it — build the buffer first, return the variable — while returning a buffer variable, a buffer parameter, or another buffer-returning call is untouched. Regression tests &lt;code&gt;tests/compile_fail/099_return_buffer_text_literal.vox&lt;/code&gt; and &lt;code&gt;100_return_buffer_text_variable.vox&lt;/code&gt;, both proven to segfault on unfixed &lt;code&gt;main&lt;/code&gt;, plus the passing control &lt;code&gt;tests/bug53_return_buffer_variable.vox&lt;/code&gt;. Found by the vox-fuzz Functions claim ledger (discrepancies D7 and D8).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A collection element read into a variable of another type is a compile error, not a segfault&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#54&lt;/a&gt;) — &lt;code&gt;a list called counts is [1, 2].&lt;/code&gt; then &lt;code&gt;label is element 1 of counts.&lt;/code&gt; into a &lt;code&gt;text&lt;/code&gt; crashed the generated program (139) with no output at all, and the reverse direction — a text element into a &lt;code&gt;number&lt;/code&gt; — silently printed &lt;code&gt;4198536&lt;/code&gt;, an address. Printing the read directly was always correct, so the read, its bounds check and its tag dispatch all worked; it was the copy into a differently-typed slot that broke. The analyzer answered &amp;quot;can&amp;apos;t prove it, allow it&amp;quot; for every element, &lt;code&gt;&amp;apos;s first&lt;/code&gt;/&lt;code&gt;&amp;apos;s last&lt;/code&gt;, byte and map read, and codegen then emitted a plain quadword move, so &lt;code&gt;Print&lt;/code&gt; picked its printer from the destination&amp;apos;s declared type and walked a number as a &lt;code&gt;char *&lt;/code&gt;. LANGUAGE.md:530-541 fixes a variable&amp;apos;s type at its declaration and says every form that writes to a declared name is checked the same way: a homogeneous list literal&amp;apos;s proven element type now reaches both the assignment type lock and the declaration site, which had no type check at all, and a &lt;code&gt;For each&lt;/code&gt; loop variable over a proven list carries it too. The proof is only offered where it holds — a list that any &lt;code&gt;Append&lt;/code&gt;, element write, whole-list assignment, call argument or copy could widen gets no element type, and a mixed list still reads through &lt;code&gt;value&lt;/code&gt; unchanged. Regression tests &lt;code&gt;tests/compile_fail/106_element_number_list_into_text.vox&lt;/code&gt; through &lt;code&gt;112_foreach_element_into_mistyped_variable.vox&lt;/code&gt;, plus the passing controls &lt;code&gt;tests/bug54_element_read_typecheck.vox&lt;/code&gt; and &lt;code&gt;tests/bug54_helper_widens_a_list.vox&lt;/code&gt;. Found by the vox-fuzz Variables claim ledger (discrepancy D1).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;treating&lt;/code&gt; clause whose types do not match the collection is a compile error instead of a segfault&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#55&lt;/a&gt;) — &lt;code&gt;print each item from [&amp;quot;a&amp;quot;] treating 98 as 31.&lt;/code&gt;, one line, compiled clean and died with 139; over a named text list it crashed identically, and over a range (&lt;code&gt;each step from 1 to 3 treating &amp;quot;a&amp;quot; as &amp;quot;b&amp;quot;&lt;/code&gt;) the clause silently never fired. The analyzer already compared a &lt;code&gt;treating&lt;/code&gt; clause&amp;apos;s match against its replacement, but never against the thing being substituted: &lt;code&gt;infer_simple_expr_type&lt;/code&gt; answers &lt;code&gt;None&lt;/code&gt; for a plain name, so the loop variable — which holds an element of the collection — was invisible to the check. Codegen was meanwhile confident enough to pick the text comparison from the subject&amp;apos;s type alone and hand &lt;code&gt;_str_eq&lt;/code&gt; the number 98 as a &lt;code&gt;char *&lt;/code&gt;. The subject&amp;apos;s type is now resolved through &lt;code&gt;named_value_type&lt;/code&gt;, which reads the element type an &lt;code&gt;each&lt;/code&gt; loop records for its variable, and a list literal in the loop header supplies one the same way a named list already did. Where the element type cannot be proven — a list widened by a later &lt;code&gt;Append&lt;/code&gt; — codegen no longer dereferences a match value that cannot be text, so the substitution simply never fires instead of faulting. A mixed list is left to the runtime, as &lt;code&gt;value&lt;/code&gt; always is. Found by the vox-fuzz basics-expansion claim ledger (discrepancies D3 and D4), master-reproduced.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;all the numbers from/between …&lt;/code&gt; no longer segfaults outside a loop header, and both spellings now include their end bound&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#56&lt;/a&gt;) — &lt;code&gt;For each step in all the numbers between 1 and 3,&lt;/code&gt; crashed the generated program (exit 139), and so did &lt;code&gt;a list called steps is all the numbers from 1 to 3.&lt;/code&gt; followed by &lt;code&gt;Print steps.&lt;/code&gt;, which printed &lt;code&gt;[&lt;/code&gt; and died. The same phrase printed straight out gave &lt;code&gt;0&lt;/code&gt;, and as an arithmetic operand gave the other operand back. LANGUAGE.md:4716 names the phrase a &lt;strong&gt;range&lt;/strong&gt;, and :262 says a range is &amp;quot;&lt;strong&gt;not&lt;/strong&gt; allocated as lists - they compile directly to efficient loop constructs&amp;quot;, so it has no value to emit: codegen&amp;apos;s arm for it emitted nothing at all and left the accumulator&amp;apos;s previous contents to be stored in a list slot or dereferenced as a list header. Every &lt;code&gt;For each&lt;/code&gt; header handed a range now routes to the range loop through the same helper loop expansion has always used — which is why &lt;code&gt;Print each step from all the numbers between 1 and 3.&lt;/code&gt; was the one spelling that worked — and a range anywhere a value is expected is a compile error naming the documented spelling, &lt;code&gt;For each n from 1 to 3,&lt;/code&gt;. Separately, that one working position was answering wrongly: the parse site read inclusiveness off which preposition was written, so &lt;code&gt;all the numbers from 1 to 3&lt;/code&gt; yielded &lt;code&gt;1 2&lt;/code&gt; while &lt;code&gt;between 1 and 3&lt;/code&gt; yielded &lt;code&gt;1 2 3&lt;/code&gt;, against :277&amp;apos;s &amp;quot;Ranges are &lt;strong&gt;inclusive&lt;/strong&gt;&amp;quot;. Both spellings are one range, and both now reach their end. Regression tests &lt;code&gt;tests/361_foreach_over_all_the_numbers.vox&lt;/code&gt; and &lt;code&gt;tests/362_all_the_numbers_is_inclusive.vox&lt;/code&gt; plus three compile-fail fixtures, proven to crash or answer wrongly on 0.4.8 and to pass after. Found by the vox-fuzz keywords claim ledger (discrepancies D5, D6 and D7).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;nothing&lt;/code&gt; in a concretely-typed slot is now a compile error instead of a segfault or a silent &lt;code&gt;0&lt;/code&gt;&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#57&lt;/a&gt;) — &lt;code&gt;a text called t is nothing.&lt;/code&gt; followed by &lt;code&gt;Print t.&lt;/code&gt; crashed the generated program (exit 139); a list did the same after printing &lt;code&gt;[&lt;/code&gt;, and a map after &lt;code&gt;{&lt;/code&gt;. The declaration alone was harmless — the read was the fault, which put the crash a line away from its cause. LANGUAGE.md:2660-2661 says where the literal may sit, &amp;quot;a list slot, a map value, or a &lt;code&gt;value&lt;/code&gt; parameter or return&amp;quot;, and the bare-&lt;code&gt;Create&lt;/code&gt; defaults table (:489-501) gives &lt;code&gt;nothing&lt;/code&gt; to &lt;code&gt;value&lt;/code&gt; alone, so a &lt;code&gt;text&lt;/code&gt;/&lt;code&gt;list&lt;/code&gt;/&lt;code&gt;map&lt;/code&gt; variable has no representation for it: codegen stored the literal&amp;apos;s payload, 0, with no tag beside it, and the read dispatched on the declared type and dereferenced a null pointer. The quiet half was the same defect wearing a plausible answer — a &lt;code&gt;number&lt;/code&gt; initialised to &lt;code&gt;nothing&lt;/code&gt; printed &lt;code&gt;0&lt;/code&gt;, against :2685&amp;apos;s &amp;quot;&lt;strong&gt;&lt;code&gt;nothing&lt;/code&gt; is not zero&lt;/strong&gt;&amp;quot; and against the compile error &lt;code&gt;nothing add 1&lt;/code&gt; already raised for exactly that reason. The literal is now refused wherever it is written into a slot of any concrete type: a declaration, an assignment, a call argument, and a return, each diagnostic naming the type and offering both ways out — a &lt;code&gt;value&lt;/code&gt;, or that type&amp;apos;s own empty value. Every position the manual does give the literal is untouched, pinned by &lt;code&gt;tests/363_nothing_in_its_documented_places.vox&lt;/code&gt;, whose output is byte-identical before and after; seven compile-fail fixtures (&lt;code&gt;tests/compile_fail/119&lt;/code&gt;-&lt;code&gt;125&lt;/code&gt;) cover the rejections, each proven to crash or answer wrongly on 0.4.8+#49-#56. The same crash reached at run time — through a &lt;code&gt;value&lt;/code&gt;, or a collection whose element type cannot be proven — is recorded in BUGS_FOUND and not fixed here. Found by the vox-fuzz random-literals worker&amp;apos;s probes (§4 D1), master-reproduced.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A buffer declared from a text-valued property keeps its type — and, on &lt;code&gt;Set&lt;/code&gt;, its bounds&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#58&lt;/a&gt;) — &lt;code&gt;a buffer called home is environment&amp;apos;s &amp;quot;HOME&amp;quot;.&lt;/code&gt; copied the bytes in correctly and then lost the name&amp;apos;s type, so &lt;code&gt;Print home.&lt;/code&gt; printed an empty line and &lt;code&gt;home&amp;apos;s size&lt;/code&gt; answered &lt;code&gt;-1&lt;/code&gt;; the same for &lt;code&gt;environment&amp;apos;s first&lt;/code&gt;/&lt;code&gt;last&lt;/code&gt; and every &lt;code&gt;arguments&amp;apos;s&lt;/code&gt; positional, while the two-step spelling through a &lt;code&gt;text&lt;/code&gt; variable was always right. LANGUAGE.md:531 says &amp;quot;&lt;strong&gt;A variable&amp;apos;s type is fixed at its declaration and never changes&lt;/strong&gt;&amp;quot;, and :3285 explains a buffer reports &lt;code&gt;(static)&lt;/code&gt; precisely because &amp;quot;the compiler knows the type from the declaration&amp;quot; — but codegen re-read the type off the initializer&amp;apos;s shape, saw an environment or argument read, and re-labelled the buffer as text, so every later read dispatched as text and &lt;code&gt;size&lt;/code&gt; fell through to the file fallback &lt;code&gt;_file_size&lt;/code&gt;. The &lt;code&gt;Set b to &amp;lt;property&amp;gt;&lt;/code&gt; spelling was worse than a wrong answer: the decision to treat the destination as a buffer is read back out of the same table, so a re-labelled buffer stopped copying bytes into its struct and stored the raw &lt;code&gt;argv&lt;/code&gt; pointer over the buffer pointer — &lt;code&gt;capacity&lt;/code&gt; then read the argument&amp;apos;s own bytes as the capacity, no position could exceed it, and &lt;code&gt;Set byte N of b&lt;/code&gt; wrote into the process&amp;apos;s argument block, segfaulting at a large &lt;code&gt;N&lt;/code&gt;. The declare-with-initializer arm now carries the guard the bare-assignment arm beside it already had. Regression tests &lt;code&gt;tests/364_buffer_from_named_environment_variable.vox&lt;/code&gt;, &lt;code&gt;365_buffer_from_positional_environment_property.vox&lt;/code&gt;, &lt;code&gt;366_buffer_from_argument_property.vox&lt;/code&gt; and &lt;code&gt;367_set_buffer_to_argument_keeps_its_bounds.vox&lt;/code&gt;, proven to answer wrongly or segfault on 0.4.8 and to pass after. Found by the vox-fuzz environment claim ledger (discrepancy D1) and re-found by the fuzzer&amp;apos;s environment leaves (ASSERT ENV-03/ENV-06).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The documented file property &lt;code&gt;exists&lt;/code&gt; is now a clear compile error instead of a bare parser complaint&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#38&lt;/a&gt;) — &lt;code&gt;Print h&amp;apos;s exists.&lt;/code&gt; on an open handle failed with &lt;code&gt;Expected property name, got Exists&lt;/code&gt;, which named the token but not the problem. &lt;code&gt;exists&lt;/code&gt; described an open handle, but a handle that opened successfully already proves the file exists, so the property answered nothing; LANGUAGE.md&amp;apos;s File Properties table has been corrected to drop the row and document the idiom that answers the real question — open the path inside an &lt;code&gt;On error&lt;/code&gt; handler — with a worked example covering both an existing and a missing path. The parser now names that idiom directly when &lt;code&gt;exists&lt;/code&gt; appears in property position. A path-level &lt;code&gt;exists&lt;/code&gt; predicate remains a planned future addition, noted in the manual rather than promised as syntax. Regression tests &lt;code&gt;tests/compile_fail/141_file_handle_exists_property.vox&lt;/code&gt; (the diagnostic) and &lt;code&gt;tests/390_file_exists_idiom.vox&lt;/code&gt; (the documented idiom, both branches).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A list or map interpolated into a format string renders everywhere, not just in &lt;code&gt;Print&lt;/code&gt;&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#44&lt;/a&gt;) — &lt;code&gt;Print &amp;quot;{flat}&amp;quot;&lt;/code&gt; gave &lt;code&gt;[1, 2, 3]&lt;/code&gt;, but &lt;code&gt;a text called captured is &amp;quot;{flat}&amp;quot;.&lt;/code&gt; and &lt;code&gt;copy &amp;quot;{flat}&amp;quot; to sink.&lt;/code&gt; gave &lt;code&gt;140237428518912&lt;/code&gt;: the collection&amp;apos;s heap address, formatted as a decimal integer, and a different address on every run. Maps behaved identically. LANGUAGE.md:3133-3136 says a format string used as a value materializes into a fresh string, and :3157-3163 says every string-taking statement accepts one and all sinks render identically — neither with a type restriction. &lt;code&gt;Print&lt;/code&gt; special- cased &lt;code&gt;List&lt;/code&gt; and &lt;code&gt;Map&lt;/code&gt; inside its own emitter, while every other sink went through &lt;code&gt;emit_append_runtime_value_to_buffer_ptr&lt;/code&gt;, which had arms for &lt;code&gt;Buffer&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt; and &lt;code&gt;Float&lt;/code&gt; and no arm for a collection, so both fell to the integer formatter. Rather than write a second, buffer- shaped renderer, the existing one was redirected: &lt;code&gt;_list_print&lt;/code&gt; and &lt;code&gt;_map_print&lt;/code&gt; now emit through &lt;code&gt;RENDER_*&lt;/code&gt; macros that consult a &lt;code&gt;_render_sink&lt;/code&gt;, which is zero for stdout and a buffer pointer otherwise, so the text initializer, buffer &lt;code&gt;set&lt;/code&gt;/&lt;code&gt;copy&lt;/code&gt;/&lt;code&gt;append&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, filesystem paths, &lt;code&gt;treating&lt;/code&gt; clauses and function arguments all render through the very routine &lt;code&gt;Print&lt;/code&gt; calls. Nested lists, empty collections, a mixed list&amp;apos;s quoted strings and floats, and a cyclic list&amp;apos;s &lt;code&gt;...&lt;/code&gt; truncation therefore come out identical in every sink. A whole &lt;code&gt;thing&lt;/code&gt; in a text initializer is still the compile error LANGUAGE.md:1224-1227 documents — a thing&amp;apos;s fields are written out at compile time and it has no runtime renderer to redirect. Found while fixing this: &lt;code&gt;{&amp;apos;the running total&amp;apos;}&lt;/code&gt; (a quoted name) and &lt;code&gt;{[1, 2]}&lt;/code&gt; (a bare literal) parse as expressions, not variable parts, and &lt;code&gt;Print&lt;/code&gt;&amp;apos;s expression arm had a &lt;code&gt;Map&lt;/code&gt; case but never a &lt;code&gt;List&lt;/code&gt; one, so those two spellings printed an address in &lt;code&gt;Print&lt;/code&gt; position too; that arm&amp;apos;s list twin is here. Six regression tests, &lt;code&gt;tests/368_…&lt;/code&gt; through &lt;code&gt;tests/373_…&lt;/code&gt;, one per sink, all proven to print heap addresses on unfixed &lt;code&gt;main&lt;/code&gt; and to pass after. Found by the vox-fuzz collections-a claim ledger (discrepancy D7) and adjudicated by the language lawyer.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A call with no declared return type is a compile error where nothing supplies one, instead of being read as a number&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#45&lt;/a&gt;) — &lt;code&gt;To &amp;apos;opaque label&amp;apos;. Return &amp;quot;hi&amp;quot;.&lt;/code&gt; followed by &lt;code&gt;print &amp;apos;opaque label&amp;apos;.&lt;/code&gt; printed &lt;code&gt;4198488&lt;/code&gt;, the rodata address of &lt;code&gt;&amp;quot;hi&amp;quot;&lt;/code&gt;; routed through a declared &lt;code&gt;text&lt;/code&gt; first it printed &lt;code&gt;hi&lt;/code&gt;. The returned value was always intact — the read was wrong, and wrong precisely where nothing supplied a type. The same confusion reached a list slot (&lt;code&gt;append &amp;apos;opaque label&amp;apos; to items.&lt;/code&gt; then &lt;code&gt;print element 1&lt;/code&gt;, giving &lt;code&gt;4210906&lt;/code&gt;, stable across runs so the wrong answer looked like data), a map value, a list literal, &lt;code&gt;set element&lt;/code&gt;, a &lt;code&gt;{...}&lt;/code&gt; interpolation and a &lt;code&gt;value&lt;/code&gt; declaration. LANGUAGE.md:649-660 names this exact shape — &amp;quot;a function pointer, printed as a number, silently. No error, no warning; the program runs and gives a wrong answer that looks like data&amp;quot; — as the thing the 0.3.0 identifier/literal split was written to kill, so guessing &lt;code&gt;number&lt;/code&gt; and staying silent was the one option the language&amp;apos;s own philosophy ruled out. The analyzer now refuses the read and names both ways out: declare the return type, or assign the result to a declared variable first. The rejection is scoped to positions that supply no type of their own — a declared variable, a reassignment to one, an argument landing on a declared parameter, and a comparison against a typed operand were never broken and are untouched. Seven compile-fail fixtures and three passing controls, proven wrong on 0.4.8 and right after; the mixed-list section of LANGUAGE.md, which documented the old guess and gave a worked example of it, is rewritten, and a &amp;quot;Reading a result&amp;quot; subsection states the rule under Functions. Full runtime tag propagation (stage 1d) is what would let such a call carry its own tag. Found by the vox-fuzz collections-a claim ledger (discrepancy D5) and adjudicated by the language lawyer, who found the defect broader than the mixed-list case the ledger reported.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A diagnostic&amp;apos;s caret no longer lands in a comment, in a text literal, or in the middle of a longer word&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#46&lt;/a&gt;) — a three-line program whose first line is the comment &lt;code&gt;(mentions hello here)&lt;/code&gt; reported its &lt;code&gt;Unknown variable: hello&lt;/code&gt; at &lt;code&gt;1:11&lt;/code&gt;, pointing inside the comment instead of at the &lt;code&gt;hello&lt;/code&gt; on line 3. &lt;code&gt;Print &amp;quot;hello&amp;quot;.&lt;/code&gt; above the same use captured the caret the same way, and a one-letter name anchored inside a longer word: with &lt;code&gt;print &amp;quot;counting&amp;quot;.&lt;/code&gt; above it, &lt;code&gt;append 1 to n.&lt;/code&gt; put its caret on the &lt;code&gt;n&lt;/code&gt; of &lt;code&gt;print&lt;/code&gt;. The analyzer locates these errors by searching the raw source for the name — the AST carries no span for an identifier — and the search had no idea what it was reading: first textual hit anywhere, substring match, comments and literals included. It is a trap laid for whoever documents their repro, since a header comment naming the construct under test is exactly what an unlucky first hit looks like. The scan is now region-aware and whole-word: every byte of the source is classified the way the lexer itself reads it — nested and multi-line &lt;code&gt;( … )&lt;/code&gt; comments, text literals with their escapes, character literals and quoted identifiers — a name mentioned in a comment is never a caret, and a hit in real code always outranks one inside a literal. Interpolation is untouched: a name that only ever appears as &lt;code&gt;{name}&lt;/code&gt; inside a text still anchors there. The classifier is pinned against the lexer by a test asserting no token the lexer emits can start inside what the classifier calls a comment. Regression tests &lt;code&gt;tests/compile_fail/137_caret_skips_a_comment_mention.vox&lt;/code&gt; through &lt;code&gt;140_caret_skips_a_multi_line_comment_mention.vox&lt;/code&gt;. Found by the language lawyer while adjudicating the vox-fuzz collections-a claim ledger, whose every probe file mis-pointed this way.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A buffer put into a text no longer needs the cast, and never yields the buffer&amp;apos;s header&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#51&lt;/a&gt;) — &lt;code&gt;a text called t is b.&lt;/code&gt; stored the buffer&amp;apos;s struct pointer in the text slot, so &lt;code&gt;Print t.&lt;/code&gt; read the 24-byte header as a C string and printed the capacity field&amp;apos;s low byte: &lt;code&gt;@&lt;/code&gt; for a 64-byte buffer, &lt;code&gt;A&lt;/code&gt; for a 65-byte one. It was stable across mutation — clearing and refilling &lt;code&gt;b&lt;/code&gt; changed nothing, because the text never touched the data — so the only symptom was one wrong character that had looked the same since it was written. Four more spellings put a cast-free buffer into a text slot; &lt;code&gt;&amp;apos;show it&amp;apos; with b.&lt;/code&gt; into a &lt;code&gt;text&lt;/code&gt; parameter and &lt;code&gt;Return a text, b.&lt;/code&gt; were wrong the same way, while &lt;code&gt;Set t to b.&lt;/code&gt; and &lt;code&gt;the t is b.&lt;/code&gt; were refused by the type lock, which named a cast that would not have changed what the sentence meant. LANGUAGE.md&amp;apos;s Basic Conversions table gives &lt;code&gt;buffer → text&lt;/code&gt; one meaning — &amp;quot;a copy of the buffer&amp;apos;s bytes&amp;quot; — and the language designer ruled that the cast-free spellings say the same thing, so all five now make that copy and &lt;code&gt;b as text&lt;/code&gt; and &lt;code&gt;&amp;quot;{b}&amp;quot;&lt;/code&gt; still agree with them word for word. The copy the cast emitted inline became &lt;code&gt;emit_buffer_to_text_copy&lt;/code&gt;, called from one place by all of them rather than written out per site, which is how #58&amp;apos;s two spellings drifted apart. Type immutability is untouched: &lt;code&gt;t&lt;/code&gt; is text before the write and text after it, and every other mismatched write is still a compile error. Regression tests &lt;code&gt;tests/385_text_from_buffer_copies.vox&lt;/code&gt;, &lt;code&gt;386_text_from_buffer_at_every_write_site.vox&lt;/code&gt; and &lt;code&gt;387_text_from_buffer_is_an_independent_copy.vox&lt;/code&gt; — the last pinning #41&amp;apos;s promise through #51&amp;apos;s spelling, that clearing, refilling and resizing the buffer leave the text exactly as it was. Found by the vox-41 fix worker probing sibling forms of bug #41.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;treating&lt;/code&gt; clause over a mixed list keeps each element&amp;apos;s runtime tag&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#59&lt;/a&gt;) — &lt;code&gt;print each item from [1, &amp;quot;a&amp;quot;] treating &amp;quot;a&amp;quot; as &amp;quot;b&amp;quot;.&lt;/code&gt; printed &lt;code&gt;1&lt;/code&gt; then &lt;code&gt;4198536&lt;/code&gt;, the text&amp;apos;s own address; so did &lt;code&gt;treating 98 as 31&lt;/code&gt;, and so did &lt;code&gt;treating 1 as 9&lt;/code&gt;, which substituted the number correctly and then printed the &lt;code&gt;&amp;quot;a&amp;quot;&lt;/code&gt; it had not touched as a pointer. The same list with no clause printed &lt;code&gt;1&lt;/code&gt; then &lt;code&gt;a&lt;/code&gt;. LANGUAGE.md promises a mixed list&amp;apos;s elements &amp;quot;carry a small per-slot type tag at runtime, so every element prints and reads back as what it is&amp;quot; (:2226-2228) and names iteration among the reads that respect it (:2236), while the clause itself replaces an element only &amp;quot;if the loop variable equals &lt;code&gt;&amp;lt;match&amp;gt;&lt;/code&gt;&amp;quot; (:424) — so an element the clause never matches must print unchanged. Wrapping the loop variable in &lt;code&gt;treating&lt;/code&gt; reported the subject&amp;apos;s static type, which a mixed list does not have: the comparison became a raw pointer &lt;code&gt;cmp&lt;/code&gt; (so a text element never matched a text match) and the result reached Print untagged (so it was rendered as an integer). Where the subject carries a runtime tag, the clause now dispatches on it — a differing tag means the substitution cannot fire and nothing is read through the match, an agreeing tag compares text by bytes and everything else in registers, and a replacement that fires carries its own tag. The value and its tag come out under the same contract a bare element read has, so Print, &lt;code&gt;value&lt;/code&gt; parameter passing and an &lt;code&gt;is a&lt;/code&gt; guard downstream all see what the element really is. This is the runtime half of #55, which rejected the provable mismatch and left the mixed list to the runtime; a subject with a static type keeps the static path untouched. Regression tests &lt;code&gt;tests/400_treating_a_mixed_list_keeps_each_tag.vox&lt;/code&gt; through &lt;code&gt;406_treating_survives_an_is_a_guard_downstream.vox&lt;/code&gt;, proven to print pointers on 0.4.8+#49-#58 and to pass after, with #55&amp;apos;s &lt;code&gt;359&lt;/code&gt;/&lt;code&gt;360&lt;/code&gt; unchanged as controls. Found by the #55 fix worker (REPORT-55 §6), master-reproduced.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;{f:.N}&lt;/code&gt; prints N correctly-rounded decimal places for any N&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#60&lt;/a&gt;) — &lt;code&gt;{pi:.17}&lt;/code&gt; was right, &lt;code&gt;{pi:.18}&lt;/code&gt; printed &lt;code&gt;3.141589999999999872&lt;/code&gt; where the double&amp;apos;s exact expansion says &lt;code&gt;…883&lt;/code&gt;, &lt;code&gt;{pi:.19}&lt;/code&gt; printed &lt;code&gt;4.-8584100000000001280&lt;/code&gt; — a wrong integer part with a &lt;code&gt;-&lt;/code&gt; inside the fraction — and from &lt;code&gt;{pi:.20}&lt;/code&gt; the digits were &lt;code&gt;3.0-9223372036854775808&lt;/code&gt;, &lt;code&gt;i64::MIN&lt;/code&gt; spliced in whole. The same sentinel was the integer part of every value at or beyond 2^63, so &lt;code&gt;{big:.2}&lt;/code&gt; on 1e22 printed &lt;code&gt;-9223372036854775808.-9223372036854775808&lt;/code&gt;. LANGUAGE.md:3106 documents &amp;quot;N decimal places&amp;quot; with no bound on N, and a double — being a binary fraction — always has an exact finite decimal expansion to print them from. The old routine scaled the whole fraction by 10^N and converted it in one step, three ways and wrong three ways: a &lt;code&gt;mulsd&lt;/code&gt;-by-10 loop that accumulated rounding error, a 10^N carry threshold built with &lt;code&gt;imul&lt;/code&gt; that wrapped negative past &lt;code&gt;i64::MAX&lt;/code&gt;, and a &lt;code&gt;cvttsd2si&lt;/code&gt; that returns the SSE &amp;quot;integer indefinite&amp;quot; value once its source overflows. Nothing is scaled now: the value is taken apart as &lt;code&gt;m * 2^e&lt;/code&gt;, an integer part at or above 2^52 comes from the same big-integer digit routine the default float printer uses (so &lt;code&gt;{f}&lt;/code&gt; and &lt;code&gt;{f:.N}&lt;/code&gt; agree on every value), and the fraction&amp;apos;s digits are produced one at a time by repeated exact halving in decimal. Rounding happens once, on those digits, with an exact tie going to the even digit, as &lt;code&gt;printf&lt;/code&gt; does — so &lt;code&gt;{9.9999:.0}&lt;/code&gt; is now &lt;code&gt;10&lt;/code&gt; where it used to truncate to &lt;code&gt;9&lt;/code&gt;. Checked digit-for-digit against glibc &lt;code&gt;printf(&amp;quot;%.*f&amp;quot;)&lt;/code&gt; over 979 value/precision pairs, including the smallest subnormal (1074 places), the largest double, the 2^52/2^53/2^63 boundaries and N up to 1500. Regression test &lt;code&gt;tests/410_float_precision_any_places.vox&lt;/code&gt;, proven to print the corrupt bands on 0.4.8 and to pass after. Found by the vox-fuzz literals worker&amp;apos;s format-specifier probes (§4 D2), master-reproduced.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A pad width is honoured at any size it can be written, and is written a page at a time&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#61&lt;/a&gt;) — &lt;code&gt;{n:2147483648}&lt;/code&gt; printed with no padding at all and no diagnostic, because the width was read with &lt;code&gt;parse::&amp;lt;i32&amp;gt;()&lt;/code&gt; inside an &lt;code&gt;if let&lt;/code&gt; with no else arm: one past &lt;code&gt;i32::MAX&lt;/code&gt; the parse failed, the &lt;code&gt;Err&lt;/code&gt; was dropped, and the spec came out identical to a bare &lt;code&gt;{n}&lt;/code&gt;. A width that did parse was then padded one &lt;code&gt;write(2)&lt;/code&gt; per character — about 265 KB/s, which is why &lt;code&gt;{n:1000000}&lt;/code&gt; took two and a half seconds and &lt;code&gt;{n:100000000}&lt;/code&gt; took nearly five minutes. LANGUAGE.md:3107-3108 puts no ceiling on a width, and none is intended: a width is a character count, rendered in full. So the count is now read as an &lt;code&gt;i64&lt;/code&gt; and reaches the printer whatever it is — &lt;code&gt;{n:2147483648}&lt;/code&gt; pads, in 1.3 s — and a count too large to hold is a compile error naming the limit instead of a silent no-op. The padding is written a 4096-byte page at a time (100 000 000 characters: 283 s before, 0.065 s after), through a writer that resumes after a short write. The same parse fed the zero-pad and the hex, binary and octal width forms, and the same per-character loop was in all four padded printers; &lt;code&gt;{f:.N}&lt;/code&gt; had the identical dropped &lt;code&gt;Err&lt;/code&gt; on its precision. All of them are fixed together. Regression tests &lt;code&gt;tests/411_pad_width_any_size.vox&lt;/code&gt;, &lt;code&gt;tests/compile_fail/169_pad_width_past_what_vox_can_count.vox&lt;/code&gt;, &lt;code&gt;170_decimal_precision_past_what_vox_can_count.vox&lt;/code&gt; and four codegen tests that pin the emitted width without writing two billion spaces. Found by the vox-fuzz literals worker&amp;apos;s format-specifier probes (§4 D3), master-reproduced and root-caused against source.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;.lib&lt;/code&gt; entry with no &lt;code&gt;, returning&lt;/code&gt; clause can no longer be read as a value&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#62&lt;/a&gt;) — &lt;code&gt;a number called n is greet.&lt;/code&gt; against a library whose Table of Contents says &lt;code&gt;To greet.&lt;/code&gt; compiled clean and answered &lt;code&gt;1&lt;/code&gt;, the leftover in the return register from a function that never set it. LANGUAGE.md:4963-4965 says a missing &lt;code&gt;returning&lt;/code&gt; clause &amp;quot;means the function returns nothing&amp;quot;, and step 5 of consuming a library (LANGUAGE.md:4990) promises calls &amp;quot;type-check like any other function&amp;quot; — a promise the parameter side already kept (arity and argument types are both checked at the call site) and the result side did not, on the identical &lt;code&gt;see&lt;/code&gt;. Reading such a call&amp;apos;s result is now a compile error naming the entry, pointing at the use, and offering both ways out: declare the return in the &lt;code&gt;.lib&lt;/code&gt;, or call the function as a statement. Statement calls of a void export — the reason to export one — are untouched. Regression test &lt;code&gt;see/void-result&lt;/code&gt; in &lt;code&gt;test.sh&lt;/code&gt; (A4.5). Found by the vox-fuzz libraries claim ledger (Discrepancy 4) and filed on the language designer&amp;apos;s ruling.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A procedure&amp;apos;s non-existent result can no longer be used as a value&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#63&lt;/a&gt;) — &lt;code&gt;To ping. Print &amp;quot;pong&amp;quot;.&lt;/code&gt; followed by &lt;code&gt;print ping.&lt;/code&gt; printed &lt;code&gt;pong&lt;/code&gt; and then &lt;code&gt;1&lt;/code&gt;, and &lt;code&gt;a number called n is ping.&lt;/code&gt; put that same &lt;code&gt;1&lt;/code&gt; into &lt;code&gt;n&lt;/code&gt;: not an answer, just whatever the call left behind. The Functions section defines the parameterless procedure (LANGUAGE.md:684-686) and gives it a home as a statement (LANGUAGE.md:779-785), but never hands a value back from a function that returns none — and LANGUAGE.md:656-660 names this exact shape, &amp;quot;a wrong answer that looks like data&amp;quot;, as what the language exists to refuse. The analyzer&amp;apos;s signature pre-pass now records every &lt;code&gt;To&lt;/code&gt; with no value-returning &lt;code&gt;Return&lt;/code&gt; at any depth, and reading one&amp;apos;s result — in a declaration, a &lt;code&gt;print&lt;/code&gt;, a list slot, a map value, a format hole, a &lt;code&gt;value&lt;/code&gt;, a comparison, an argument to another call, a &lt;code&gt;Set&lt;/code&gt;, an &lt;code&gt;Append&lt;/code&gt; or a &lt;code&gt;Return&lt;/code&gt; — is a compile error naming the function and offering both ways out. A bare &lt;code&gt;Return.&lt;/code&gt; counts as returning nothing; a function that does return a value without declaring its type is a different bug (#45) and is untouched here. Calling a procedure as a statement stays legal. Regression tests &lt;code&gt;tests/compile_fail/158_procedure_result_in_a_declaration.vox&lt;/code&gt; through &lt;code&gt;168_bare_return_result_used.vox&lt;/code&gt;, plus the passing controls &lt;code&gt;tests/407_procedure_called_as_a_statement.vox&lt;/code&gt; and &lt;code&gt;tests/408_declared_return_used_as_a_value.vox&lt;/code&gt;. Found by the #45 fix worker while closing #45.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;the h&amp;apos;s descriptor&lt;/code&gt; reads the property, like &lt;code&gt;h&amp;apos;s descriptor&lt;/code&gt; always did&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#64&lt;/a&gt;) — &lt;code&gt;the&lt;/code&gt; is only an article before a variable name (LANGUAGE.md:1857, :523, :887), so the two possessive spellings are one reading. They were two lists: the parser resolved &lt;code&gt;name&amp;apos;s property&lt;/code&gt; in one arm of &lt;code&gt;parse_primary&lt;/code&gt; and &lt;code&gt;the name&amp;apos;s property&lt;/code&gt; in another, each with its own hand-written property arms, and the second knew only the time and timer properties plus &lt;code&gt;size&lt;/code&gt;/&lt;code&gt;length&lt;/code&gt;/&lt;code&gt;capacity&lt;/code&gt;/&lt;code&gt;empty&lt;/code&gt;/&lt;code&gt;full&lt;/code&gt;. Every file property but &lt;code&gt;size&lt;/code&gt;, both list-element properties, a map&amp;apos;s &lt;code&gt;keys&lt;/code&gt;/&lt;code&gt;values&lt;/code&gt; and its key read, all seven number properties and a buffer&amp;apos;s &lt;code&gt;type&lt;/code&gt; answered &lt;code&gt;Expected property name&lt;/code&gt; behind the article — while the *quoted* timer names (&lt;code&gt;t&amp;apos;s &amp;apos;start time&amp;apos;&lt;/code&gt;) and the misspelled-&lt;code&gt;arguments&lt;/code&gt; suggestion had drifted the other way and worked only *with* it. Both spellings now call one &lt;code&gt;parse_possessive_tail&lt;/code&gt;, which holds the specials, the typo diagnostic, the map-key read, every property arm, #38&amp;apos;s &lt;code&gt;exists&lt;/code&gt; explanation, the &lt;code&gt;start time&lt;/code&gt; two-word follower and the duration unit — so a property is added or diagnosed in exactly one place, the lesson #51 and #58 already taught about a second copy of a list. &lt;code&gt;the&lt;/code&gt; before a reserved word is untouched and still rejected: &lt;code&gt;arguments&lt;/code&gt;, &lt;code&gt;environment&lt;/code&gt; and &lt;code&gt;current time&lt;/code&gt; are not variable names, and the manual gives them their own &lt;code&gt;the&lt;/code&gt;-led phrases. Regression tests &lt;code&gt;tests/420_the_possessive_reads_file_properties.vox&lt;/code&gt; through &lt;code&gt;424_the_possessive_on_numbers_and_timers.vox&lt;/code&gt; and &lt;code&gt;tests/compile_fail/171_the_possessive_file_handle_exists.vox&lt;/code&gt;, all six proven to fail against the previous tree. Found by the #38 fix worker probing the file-property surface, master-confirmed.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A declaration whose initializer is the wrong type is a compile error instead of a segfault or a wrong value&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#65&lt;/a&gt;) — &lt;code&gt;a text called n is 5.&lt;/code&gt; followed by &lt;code&gt;Print n.&lt;/code&gt; dereferenced the literal 5 as a text pointer and crashed (139); &lt;code&gt;a number called n is &amp;quot;get five&amp;quot;.&lt;/code&gt; printed &lt;code&gt;4198488&lt;/code&gt;, the string&amp;apos;s own address, which is LANGUAGE.md:647-667&amp;apos;s worked example still printing the number the manual says it no longer prints. A boolean took the same pointer, a text into a float silently answered &lt;code&gt;0.0&lt;/code&gt;, a number into a list segfaulted, and a text into a map printed &lt;code&gt;{}&lt;/code&gt;. The type lock has enforced LANGUAGE.md:531-532 — &amp;quot;&lt;strong&gt;A variable&amp;apos;s type is fixed at its declaration and never changes&lt;/strong&gt;&amp;quot; — on every write to an already-declared name since 0.3.0, so &lt;code&gt;Set n to &amp;quot;x&amp;quot;.&lt;/code&gt; was refused, but nothing checked the declaration itself, which is where the type is chosen. &lt;code&gt;check_initialiser_type&lt;/code&gt; now applies the lock&amp;apos;s own compatibility rule at the declaration, and &lt;code&gt;check_argument_type&lt;/code&gt; / &lt;code&gt;check_return_type&lt;/code&gt; close the identical hole at a call&amp;apos;s argument and at a return (&lt;code&gt;greet with 5.&lt;/code&gt; on a text parameter, &lt;code&gt;Return a text, 5.&lt;/code&gt;) — the three sites bug #57 already covered for &lt;code&gt;nothing&lt;/code&gt;. The diagnostic names both types and the two ways out: redeclare, or the cast LANGUAGE.md&amp;apos;s Basic Conversions table documents. Deliberately untouched: a &lt;code&gt;value&lt;/code&gt; destination or source, a buffer&amp;apos;s content write, the &lt;code&gt;file&lt;/code&gt;/&lt;code&gt;time&lt;/code&gt;/&lt;code&gt;timer&lt;/code&gt; handles whose documented initializers are of another type (LANGUAGE.md:503-519 — &lt;code&gt;a file called source is &amp;quot;input.txt&amp;quot;.&lt;/code&gt;), a &lt;code&gt;thing&lt;/code&gt; copy, and a buffer read into a text without the cast, which is bug #51&amp;apos;s still-open question. Regression tests &lt;code&gt;tests/compile_fail/145&lt;/code&gt;–&lt;code&gt;157&lt;/code&gt;, all thirteen proven to compile-and-misbehave on 9734e5d, plus the passing controls &lt;code&gt;tests/395_declaration_initialiser_types_that_agree.vox&lt;/code&gt; and &lt;code&gt;tests/396_mistyped_initialisers_written_correctly.vox&lt;/code&gt;. Found by the #51 fix worker and by the vox-fuzz names-and-strings claim ledger (discrepancy D1).&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;a float called ratio is 3.&lt;/code&gt; holds 3.0 instead of 0.0&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#65&lt;/a&gt;) — a whole number into a float stored the integer&amp;apos;s raw bits, which the float slot then read as 1.5e-323 and printed as &lt;code&gt;0.0&lt;/code&gt;; &lt;code&gt;ratio add 1.0&lt;/code&gt; answered &lt;code&gt;1.0&lt;/code&gt; and &lt;code&gt;ratio multiply 2.0&lt;/code&gt; answered &lt;code&gt;0.0&lt;/code&gt;. Per the language designer&amp;apos;s ruling — &amp;quot;in human language we call 1 a number and pi a number; it should be the same in Vox&amp;quot; — a number and a float are one family, so the declaration converts rather than refusing, emitting the same two instructions &lt;code&gt;3 as a float&lt;/code&gt; already emitted. The analyzer also stops relabelling a declared float from its initializer&amp;apos;s shape, which is why &lt;code&gt;a float called f is 3.&lt;/code&gt; followed by &lt;code&gt;Set f to 4.0.&lt;/code&gt; used to answer &amp;quot;cannot assign float to &amp;apos;f&amp;apos;, which is a number&amp;quot; and now works. &lt;code&gt;a number called n is 3.5.&lt;/code&gt; keeps its 3.5, unchanged. The type lock still refuses &lt;code&gt;Set f to 3.&lt;/code&gt; and &lt;code&gt;Set n to 3.5.&lt;/code&gt; one line later; that is the designer&amp;apos;s own static-int64 gap, left with them, along with the float parameter and float return that still misread a whole number (both recorded under #65).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.8</title>
    <id>tag:vox-lang.dev,2026:release/0.4.8</id>
    <updated>2026-08-20T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.8"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Write of a number, float, boolean, or value is a compile error, not a segfault. Plus 8 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Write&lt;/code&gt; of a number, float, boolean, or &lt;code&gt;value&lt;/code&gt; is a compile error, not a segfault&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#40&lt;/a&gt;) — &lt;code&gt;Write n to out&lt;/code&gt; compiled and then crashed the generated program (exit 139) for all three scalars, while text, buffers, and format strings wrote correctly: &lt;code&gt;Write&lt;/code&gt; hands its operand to &lt;code&gt;FILE_WRITE_STR&lt;/code&gt;, which reads it as a pointer to text, so a scalar&amp;apos;s *value* was used as an address (n = 72 read address 72). LANGUAGE.md documents &lt;code&gt;Write&lt;/code&gt; for text, buffers, and format strings, so the analyzer now refuses a bare scalar operand the way &lt;code&gt;append&lt;/code&gt; refuses a number source, naming the operand, its type, and the spelling that works: &lt;code&gt;Write &amp;quot;{n}&amp;quot; to out.&lt;/code&gt; A &lt;code&gt;value&lt;/code&gt; operand is refused with it — it crashed the same way when it held a number or &lt;code&gt;nothing&lt;/code&gt;, and the compiler cannot tell that from the text-holding case that worked — and its message names the fix verified for every case a value can hold: copy it into a typed variable and write that. Rendering a scalar directly remains an open design option; this pass fixes the diagnostic, not the language. Found by a human writing ordinary Vox while building vox-fuzz&amp;apos;s stdin input generation.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;buffer as text&lt;/code&gt; now copies the buffer instead of aliasing it&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#41&lt;/a&gt;) — the cast returned a pointer into the buffer&amp;apos;s data area, so a text made from a buffer was a window onto it, not a value. Clearing and refilling the buffer silently rewrote every text taken from it, with no diagnostic; and because resizing a buffer frees its old allocation, as the manual&amp;apos;s Buffer Resizing section says it does, reading such a text after a resize was a use-after-free — both directions segfaulted from eight lines of ordinary code, in a language whose headline promise is memory safety. &lt;code&gt;as text&lt;/code&gt; now copies the buffer&amp;apos;s bytes into a fresh dynamic buffer, the same allocation format strings and the other text-producing casts use, so exit cleanup tracks it identically. Regression test &lt;code&gt;tests/buffer_as_text_copies.vox&lt;/code&gt;; LANGUAGE.md&amp;apos;s conversion table and Buffer Resizing notes now state that the text is an independent copy. Found while writing an ordinary Vox program that read lines from a file into a list.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A buffer&amp;apos;s &lt;code&gt;type&lt;/code&gt; property reports &lt;code&gt;Buffer (static)&lt;/code&gt; however it was declared&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#42&lt;/a&gt;) — &lt;code&gt;a buffer called b is 16 bytes in size&lt;/code&gt;, &lt;code&gt;is 16 bytes&lt;/code&gt;, &lt;code&gt;Create a buffer called b with size 16&lt;/code&gt;, and the bare dynamic &lt;code&gt;a buffer called b.&lt;/code&gt; all printed &lt;code&gt;Text (dynamic)&lt;/code&gt; from &lt;code&gt;b&amp;apos;s type&lt;/code&gt;, against LANGUAGE.md&amp;apos;s explicit listing of &lt;code&gt;buffer&lt;/code&gt; among the statically-typed kinds; only the string-initialised &lt;code&gt;is &amp;quot;seed&amp;quot;&lt;/code&gt; form was right. Every sized and dynamic spelling routes through &lt;code&gt;BufferDecl&lt;/code&gt;, which registered the variable&amp;apos;s runtime kind but never its declared type, so the property&amp;apos;s lookup missed and fell through to the runtime-tag dispatch, where a buffer pointer reads as a string tag. The declaration now registers the declared type; the same omission on &lt;code&gt;Get the current time into&lt;/code&gt; is closed alongside it so a &lt;code&gt;time&lt;/code&gt; reports &lt;code&gt;Time (static)&lt;/code&gt;. Found by the vox-fuzz buffer claim ledger (discrepancy D2) and adjudicated by the language lawyer.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A conditional &lt;code&gt;value&lt;/code&gt; return no longer segfaults the caller&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#43&lt;/a&gt;) — a function whose only &lt;code&gt;Return&lt;/code&gt; sat inside an &lt;code&gt;If&lt;/code&gt;/&lt;code&gt;Otherwise&lt;/code&gt; (&lt;code&gt;To label with a value called v. If v is a number, return a value, v. Otherwise, return a value, 99.&lt;/code&gt;) crashed its caller with SIGSEGV, deterministically: the integer &lt;code&gt;99&lt;/code&gt; was dereferenced as a &lt;code&gt;char*&lt;/code&gt; inside &lt;code&gt;_print_cstr_impl&lt;/code&gt;. The parser&amp;apos;s Gate B fed a &lt;code&gt;Return&lt;/code&gt;&amp;apos;s declared type into the function&amp;apos;s signature only for &lt;strong&gt;top-level&lt;/strong&gt; body statements, so a branch-nested &lt;code&gt;Return&lt;/code&gt; left &lt;code&gt;return_type&lt;/code&gt; at &lt;code&gt;Void&lt;/code&gt;; codegen then skipped the &lt;code&gt;value&lt;/code&gt; return&amp;apos;s r11 tag load, and the caller stored r11 into the variable&amp;apos;s tag slot anyway — r11 still holding the callee&amp;apos;s &lt;strong&gt;parameter&lt;/strong&gt; tag (text) from the &lt;code&gt;is a number&lt;/code&gt; predicate, which labelled an integer payload as text. Three changes: &lt;code&gt;emit_load_value_tag&lt;/code&gt;&amp;apos;s no-tag arm now defaults to the integer tag unless &lt;code&gt;expr_leaves_tag_in_r11&lt;/code&gt; confirms a tag was left there, which makes this class of mislabelling impossible whatever else is wrong; the parser now adopts a branch-nested &lt;code&gt;Return&lt;/code&gt;&amp;apos;s declared type as the signature when the body declared no top-level one and every declaration agrees, which is the actual cause; and a function that falls off its end now returns its declared type&amp;apos;s empty value (empty text, zero, or a &lt;code&gt;value&lt;/code&gt; tagged as the number &lt;code&gt;0&lt;/code&gt;) rather than whatever rax held, since a typed function could not previously reach that path at all. The same missing signature made the plain-type family silently wrong rather than unsafe — &lt;code&gt;Return a text&lt;/code&gt; inside a branch printed the text&amp;apos;s address as a number — and that is fixed by the same change. A function whose branches declare *different* types still has no signature to adopt and is unchanged (memory-safe, silently wrong); making that a compile error is a language decision, noted in the register. Regression test proven to segfault on the unfixed compiler and to pass after, with the single-expression &lt;code&gt;Return a value, &amp;lt;expr&amp;gt;.&lt;/code&gt; form kept as the control. LANGUAGE.md&amp;apos;s &amp;quot;One limitation to know&amp;quot; paragraph is rewritten: the factorial pattern now works for &lt;code&gt;value&lt;/code&gt; returns. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #43.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Seek ... to line N&lt;/code&gt; reaches line N&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#47&lt;/a&gt;) — every target of 2 or more landed at the start of line 2, whatever N was, and a line past the end of the file never set the error flag. &lt;code&gt;_seek_fd_line&lt;/code&gt; kept its line counter in &lt;code&gt;rcx&lt;/code&gt; across the read syscall, and &lt;code&gt;syscall&lt;/code&gt; clobbers rcx with the return address, so the counter became a code address on the very first byte read: the compare against the target failed immediately, the scan fell out at the first newline, and the past-EOF branch was unreachable. The counter now lives in &lt;code&gt;rbx&lt;/code&gt;, which is callee-saved and already pushed. &lt;code&gt;Seek ... to byte N&lt;/code&gt; was a bare &lt;code&gt;lseek&lt;/code&gt; and was never affected; &lt;code&gt;_seek_fd_line&lt;/code&gt; exists only in the x86_64 runtime. Found by the vox-fuzz files claim ledger (discrepancy D3) and adjudicated by the language lawyer.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A failed &lt;code&gt;Write&lt;/code&gt; sets the error flag, and both read forms agree about a dead handle&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#48&lt;/a&gt;) — a &lt;code&gt;Write&lt;/code&gt; to a full device, to a handle opened for reading, to a closed handle or to a handle whose &lt;code&gt;open&lt;/code&gt; failed all succeeded silently as far as Vox could tell, so a write that did not happen was indistinguishable from one that did; and &lt;code&gt;Read from&lt;/code&gt; a failed-open handle reported a zero-byte read where &lt;code&gt;Read line from&lt;/code&gt; the identical handle set the flag. The three write macros issued their &lt;code&gt;write(2)&lt;/code&gt; and popped straight past rax without inspecting it, and &lt;code&gt;FileWrite&lt;/code&gt; never touched &lt;code&gt;_last_error&lt;/code&gt; at all. Writes now record their syscall&amp;apos;s outcome — the errno for a failure, &lt;code&gt;EIO&lt;/code&gt; for a short write, zero on success — and &lt;code&gt;Write&lt;/code&gt;, &lt;code&gt;Write a newline&lt;/code&gt; and &lt;code&gt;Read from&lt;/code&gt; set the flag on a dead handle exactly as &lt;code&gt;Read line from&lt;/code&gt; already did. Found by the vox-fuzz files claim ledger (discrepancies D4 and D5) and adjudicated by the language lawyer.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;LANGUAGE.md defines buffer bounds&lt;/strong&gt; — writes accept positions 1..capacity and extend &lt;code&gt;size&lt;/code&gt; (zero-filling any gap); reads accept 1..size; position 0 is out of bounds for both. The compiler has always behaved this way and the manual&amp;apos;s own worked example relied on it, but the Bounds Checking paragraph never said so (buffer ledger discrepancy D3). The fixed-buffer feature list no longer says truncation is &amp;quot;silent&amp;quot;: it sets the error flag, as the Reading section already stated.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;LANGUAGE.md no longer says &lt;code&gt;Read&lt;/code&gt; appends&lt;/strong&gt; — the Reading section&amp;apos;s high-level bullet claimed &lt;code&gt;Read&lt;/code&gt; &amp;quot;appends incoming data to the buffer&amp;quot;, as the explicit contrast against &lt;code&gt;Read line&lt;/code&gt;&amp;apos;s &amp;quot;replaces&amp;quot;. The compiler deliberately replaces (&lt;code&gt;codegen/statements.rs&lt;/code&gt;: &amp;quot;read replaces, not appends&amp;quot;), &lt;code&gt;tests/runtime/b340_pipe_exact_fit.asm&lt;/code&gt; asserts it, and no other sentence in the manual depends on append. The bullet now says &lt;code&gt;Read&lt;/code&gt; replaces the buffer&amp;apos;s contents and continues from the file&amp;apos;s current position — which is the real contrast with &lt;code&gt;Read line&lt;/code&gt; (files ledger discrepancy D2). The Seeking, Writing and Error Handling sections now also state that a failed &lt;code&gt;Write&lt;/code&gt;, and a read on a handle whose open failed, set the error flag and are catchable.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;LANGUAGE.md collections section: five examples corrected, one annotated&lt;/strong&gt; — from the vox-fuzz collections-a claim ledger (discrepancies D1-D6), each adjudicated by the language lawyer and each recompiled against this tree. The mixed-list widening example&amp;apos;s &lt;code&gt;append hello to items&lt;/code&gt; is now &lt;code&gt;append &amp;quot;hello&amp;quot; to items&lt;/code&gt;: a bare word is an identifier (LANGUAGE.md:645-668), so the example as printed did not compile (D1). The list-of-maps paragraph no longer claims a &lt;code&gt;For each&lt;/code&gt; &amp;quot;types the loop variable as a map&amp;quot; — the loop variable is deliberately untyped and map access is a static check, so reading a key off it is a compile error; the section now shows the index-loop idiom that works (D2). The mixed-list guard idiom is replaced for the same reason: a predicate reads the runtime tag but does not narrow the static type, so the guarded element has to be extracted into a declared variable (D3), and the &lt;code&gt;item as a number&lt;/code&gt; cast the same paragraph offered as the alternative is dropped, since casting a dynamically-tagged value is a known gap and is rejected (D4). The promise that an unprovable value in a list &amp;quot;is always read back as what it is rather than silently reinterpreted&amp;quot; is hedged to match the limitation paragraph twenty lines below it, which always conceded the conservative &lt;code&gt;number&lt;/code&gt; tag guess (D5). And the cyclic-list example&amp;apos;s &lt;code&gt;(prints: [[...]] then cyclic)&lt;/code&gt; is marked as the abbreviation it is — 64 brackets each side of the &lt;code&gt;...&lt;/code&gt; (D6). Filed unfixed alongside these: &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#44&lt;/a&gt; (collections render as a raw address outside &lt;code&gt;Print&lt;/code&gt;), &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#45&lt;/a&gt; (D5&amp;apos;s compiler half) and &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#46&lt;/a&gt; (a diagnostic caret landing in a comment).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.7</title>
    <id>tag:vox-lang.dev,2026:release/0.4.7</id>
    <updated>2026-08-20T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.7"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A float at or beyond 2^63 no longer saturates when printed. Plus 4 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A float at or beyond 2^63 no longer saturates when printed&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#34&lt;/a&gt;) — &lt;code&gt;Print&lt;/code&gt;, &lt;code&gt;&amp;quot;{x}&amp;quot;&lt;/code&gt; interpolation, and &lt;code&gt;x as text&lt;/code&gt; all printed &lt;code&gt;9223372036854775808.372036854775808&lt;/code&gt; for *every* value at or past 2^63 (10000000000000000000.0 included), because the formatter&amp;apos;s integer part went through &lt;code&gt;cvttsd2si&lt;/code&gt;, which saturates to &lt;code&gt;i64::MIN&lt;/code&gt;&amp;apos;s bit pattern past &lt;code&gt;i64::MAX&lt;/code&gt; — the trailing digits were the fractional part of that same wrong, constant value, which is why they never changed. The stored double was always correct; only the print path was wrong. 2^63 is already far past 2^52, the point beyond which a double&amp;apos;s 52-bit mantissa has no room left for a fractional bit, so every affected value is an exact integer — &lt;code&gt;_print_float&lt;/code&gt; and &lt;code&gt;_buffer_append_float&lt;/code&gt; now detect the magnitude and, for that range only, extract the raw mantissa and exponent and produce the exact decimal digits by schoolbook binary-to-decimal (double the mantissa&amp;apos;s decimal digit string once per bit of exponent past 52), which is exact because no floating point is involved past reading the bits. Values below 2^63 are untouched and still use the original, already-correct path. Deliberately &lt;strong&gt;not&lt;/strong&gt; fixed in this pass: a nonzero float below the formatter&amp;apos;s fixed 15-digit fractional precision still prints &lt;code&gt;0.0&lt;/code&gt; — a lost-precision problem in a different part of the same routine, not a saturation, tracked as still-open in the register entry. Regression test proven to fail on the unfixed compiler on exactly the large-magnitude rows, with the sub-2^63, division-derived, and IEEE-754-rounding rows kept as controls that pass on both sides. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #34.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;as a number&lt;/code&gt; no longer wraps silently past i64&amp;apos;s range&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#35&lt;/a&gt;) — &lt;code&gt;&amp;quot;9223372036854775808&amp;quot; as a number&lt;/code&gt; (one past &lt;code&gt;i64::MAX&lt;/code&gt;) returned &lt;code&gt;-9223372036854775808&lt;/code&gt; with the error flag never set, so &lt;code&gt;On error&lt;/code&gt; could not catch it and the wrapped value was indistinguishable from a real one; applied to every base (&lt;code&gt;&amp;quot;ffffffffffffffffff&amp;quot; as a hex number&lt;/code&gt; gave &lt;code&gt;-1&lt;/code&gt;). Every digit in these inputs is valid for its base, so the documented &amp;quot;stops at the first invalid character&amp;quot; rule never engaged — the whole string parsed and the accumulator wrapped. &lt;code&gt;_parse_i64&lt;/code&gt;, &lt;code&gt;_parse_int_radix&lt;/code&gt;, and their length-bounded buffer variants in &lt;code&gt;coreasm/x86_64/int.asm&lt;/code&gt; now accumulate the magnitude with an unsigned &lt;code&gt;mul&lt;/code&gt; (which reports a truncated product instead of silently wrapping) and range-check the result against the sign at the end: a positive numeral must fit under &lt;code&gt;i64::MAX&lt;/code&gt;, a negative one may reach &lt;code&gt;i64::MIN&lt;/code&gt;&amp;apos;s magnitude (&lt;code&gt;2^63&lt;/code&gt;) — the two are different bounds, so a naive &amp;quot;digits &amp;gt; i64::MAX&amp;quot; check would have wrongly flagged legitimate &lt;code&gt;i64::MIN&lt;/code&gt; input, which is kept as a control. Either check failing sets the same error flag &lt;code&gt;On error&lt;/code&gt; already reads for a wholly-invalid string. The returned value on overflow is not defined (0 or a wrapped magnitude); the flag is the fix. Regression test proven to fail on the unfixed compiler on exactly the three overflow cases, with &lt;code&gt;i64::MAX&lt;/code&gt;, &lt;code&gt;i64::MIN&lt;/code&gt;, a valid hex value, and the pre-existing &lt;code&gt;&amp;quot;abc&amp;quot; as a base5 number&lt;/code&gt; raise kept as controls that pass unchanged on both sides. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #35.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A width specifier no longer changes what a value is&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#36&lt;/a&gt;) — &lt;code&gt;&amp;quot;{f:06}&amp;quot;&lt;/code&gt; on a float printed its raw IEEE-754 bit pattern (&lt;code&gt;4615063718147915776&lt;/code&gt; for 3.5), and on a &lt;code&gt;text&lt;/code&gt; printed the string&amp;apos;s &lt;strong&gt;address&lt;/strong&gt; — silent wrong data and an information leak, proven by two same-content texts printing different numbers. The type-aware dispatch in &lt;code&gt;emit_formatted_value&lt;/code&gt; was gated on there being *no* width, so writing one skipped the type check precisely when the compiler knew the type best. Non-integer types are now rendered by type whether or not a width is present. The width is not yet *applied* to floats/texts — coreasm has padding primitives only for integers and hex — so a width there is ignored rather than honoured, matching the runtime-tagged &lt;code&gt;value&lt;/code&gt; path; that cosmetic residue is recorded in the register. Regression test proven to fail on the unfixed compiler on exactly the float/text/buffer rows, with integer and boolean width rows kept as controls that pass on both sides. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #36.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A file&amp;apos;s &lt;code&gt;readable&lt;/code&gt; property now reflects its actual open mode&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#37&lt;/a&gt;) — &lt;code&gt;readable&lt;/code&gt; tested &lt;code&gt;fd &amp;gt;= 0&lt;/code&gt;, which is true for any successfully opened handle, so a file opened &lt;code&gt;for writing&lt;/code&gt; or &lt;code&gt;for appending&lt;/code&gt; still reported &lt;code&gt;readable&lt;/code&gt; as &lt;code&gt;1&lt;/code&gt;. The obvious defensive idiom, &lt;code&gt;If f&amp;apos;s readable then,&lt;/code&gt; before a read, passed on a write-only handle and the read that followed failed at the OS level. &lt;code&gt;writable&lt;/code&gt; already derived its answer correctly from the handle&amp;apos;s recorded open mode; &lt;code&gt;readable&lt;/code&gt; now shares that same source of truth instead of being a constant. Regression test opens one file for writing, appending, and reading in turn and checks &lt;code&gt;readable&lt;/code&gt;, &lt;code&gt;writable&lt;/code&gt;, and &lt;code&gt;permissions&lt;/code&gt; in each mode; proven to fail on the unfixed compiler on exactly the writing/appending &lt;code&gt;readable&lt;/code&gt; rows, with the &lt;code&gt;writable&lt;/code&gt; rows and the constant &lt;code&gt;permissions&lt;/code&gt; value kept as controls. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #37.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A format string in a collection prints as text at every position, not just the second&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#39&lt;/a&gt;) — &lt;code&gt;[&amp;quot;{base}&amp;quot;, &amp;quot;plain&amp;quot;]&lt;/code&gt; printed two raw pointers (a heap address that moved under ASLR between runs, plus a stable rodata address) instead of &lt;code&gt;core&lt;/code&gt;/&lt;code&gt;plain&lt;/code&gt;; moving the format string to the second slot made both elements print correctly, which was the tell that this was a static element-type inference bug, not a runtime-tag bug — a named list under a plain &lt;code&gt;print each&lt;/code&gt; already worked, but attaching a &lt;code&gt;treating&lt;/code&gt; clause to that *same* list broke it again. &lt;code&gt;Expr::FormatString&lt;/code&gt; had no arm in the three places that classify a list&amp;apos;s element type from its literal shape — the &lt;code&gt;for each&lt;/code&gt;/&lt;code&gt;print each&lt;/code&gt; inline-literal inference, the named-list-declaration inference that feeds &lt;code&gt;treating&lt;/code&gt;, and &lt;code&gt;element N of &amp;lt;literal&amp;gt;&lt;/code&gt; — so each fell through to its generic default (&lt;code&gt;Unknown&lt;/code&gt;, which for a literal is not the same safe fallback &lt;code&gt;Unknown&lt;/code&gt; is for a named list, since a named list&amp;apos;s &lt;code&gt;Unknown&lt;/code&gt; widens to &lt;code&gt;Mixed&lt;/code&gt; and dispatches on the still-correct runtime tag, while a literal&amp;apos;s &lt;code&gt;Unknown&lt;/code&gt; fed nothing and defaulted to &lt;code&gt;PRINT_INT&lt;/code&gt;). Bug #17 fixed this same missing arm in the two functions that back append and general expression typing; these three siblings were never given it. Fixed by adding &lt;code&gt;Expr::FormatString =&amp;gt; VarType:: String&lt;/code&gt;/&lt;code&gt;Some(VarType::String)&lt;/code&gt; to all three. Regression test covers all nine control rows (first vs. second position in an inline literal, a named list with and without &lt;code&gt;treating&lt;/code&gt;, &lt;code&gt;element N of&lt;/code&gt;, a plain &lt;code&gt;For each&lt;/code&gt;, escaped-braces-only, and a no-format-string &lt;code&gt;treating&lt;/code&gt; list); proven to fail on the unfixed compiler on exactly the format-first inline-literal, &lt;code&gt;For each&lt;/code&gt;-over-literal, and named-list-with-&lt;code&gt;treating&lt;/code&gt; rows, with the rest passing on both sides as controls. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #39.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.6</title>
    <id>tag:vox-lang.dev,2026:release/0.4.6</id>
    <updated>2026-08-20T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.6"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A period now closes a Repeat body. Plus 5 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A period now closes a &lt;code&gt;Repeat&lt;/code&gt; body&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#27&lt;/a&gt;) — the construct that &lt;code&gt;Repeat N times, &amp;lt;actions&amp;gt;.&lt;/code&gt; is a sentence-ending loop, the shape LANGUAGE.md documents for &lt;code&gt;While&lt;/code&gt; and &lt;code&gt;For each&lt;/code&gt;, never closed on a period. The continuation was silently absorbed into the loop and re-run once per iteration, with no error: &lt;code&gt;Repeat 2 times, print &amp;quot;r&amp;quot;. Print &amp;quot;after&amp;quot;.&lt;/code&gt; printed &lt;code&gt;r after r after&lt;/code&gt; (the absorbed statement run inside the loop) instead of &lt;code&gt;r r after&lt;/code&gt;. A second symptom shared the same root: a comma did not separate actions in a &lt;code&gt;Repeat&lt;/code&gt; body, so &lt;code&gt;Repeat 2 times, print &amp;quot;a&amp;quot;, print &amp;quot;b&amp;quot;.&lt;/code&gt; was a parse error at the comma — &lt;code&gt;Repeat&lt;/code&gt;&amp;apos;s body loop was missing the entire separator handling that &lt;code&gt;While&lt;/code&gt;&amp;apos;s had. Both are the same gap: the spec already promised that a period closes the innermost open clause and that &lt;code&gt;Repeat&lt;/code&gt; is one such clause, so this is a fix, not a feature. &lt;code&gt;parse_repeat&lt;/code&gt; now shares one body loop with &lt;code&gt;parse_while&lt;/code&gt; (factored into &lt;code&gt;parse_loop_body&lt;/code&gt; so the two cannot drift apart again): comma continues, period closes, blank line closes, EOF closes. &lt;code&gt;Repeat&lt;/code&gt; was also added to &lt;code&gt;parse_block&lt;/code&gt;&amp;apos;s self-terminating construct list alongside &lt;code&gt;If&lt;/code&gt;/&lt;code&gt;While&lt;/code&gt;/&lt;code&gt;For&lt;/code&gt;, so a &lt;code&gt;Repeat&lt;/code&gt; that is not the last action in a branch no longer swallows the action that follows it. Regression tests cover the period-closes case, the comma-continues case, the blank-line-closes case (the one path that already worked, kept as a guard), stacked periods closing a &lt;code&gt;Repeat&lt;/code&gt; nested in each of &lt;code&gt;For each&lt;/code&gt;, &lt;code&gt;While&lt;/code&gt;, and &lt;code&gt;If&lt;/code&gt;, a &lt;code&gt;Repeat&lt;/code&gt; inside a function followed by a statement, a nested &lt;code&gt;If&lt;/code&gt; as the last action, and the self-termination parity case. Parser-only; analyzer and codegen already handled a closed &lt;code&gt;Repeat&lt;/code&gt; correctly. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #27.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A buffer declaration always allocates&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#28&lt;/a&gt;) — a &lt;code&gt;buffer&lt;/code&gt; declared inside an &lt;code&gt;If&lt;/code&gt; body that never ran, then redeclared at top level, segfaulted. The second declaration emitted no allocation at all — only &lt;code&gt;_buffer_clear&lt;/code&gt; and &lt;code&gt;_buffer_append_bytes&lt;/code&gt; — because the name was already bound, so the only &lt;code&gt;_alloc_buffer&lt;/code&gt; sat inside the branch that did not run and the slot still held null when the clear dereferenced it. It needed both halves: neither a conditional declaration alone nor a redeclaration alone reproduced it, and only &lt;code&gt;buffer&lt;/code&gt; was affected — &lt;code&gt;number&lt;/code&gt; and &lt;code&gt;text&lt;/code&gt; in the same shape were fine, as was the sized buffer path, which already allocated on every declaration. Diagnosed from the emitted assembly rather than from the symptom: the register&amp;apos;s original guess (stack garbage dereferenced by &lt;code&gt;Print&lt;/code&gt;) was wrong, since the crash happens even when the buffer is never read. The string-initialised declaration now allocates unconditionally, as the sized path always did, while preserving sizedness — an earlier attempt that allocated a dynamic auto-growing buffer everywhere silently disabled fixed-size overflow detection language-wide, which the full suite caught and the bug&amp;apos;s own matrix did not. Twelve regression tests; eight segfault on the unfixed compiler and the rest are controls that must pass on both sides. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #28.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A string literal is data, never a name&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#29&lt;/a&gt;, &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#30&lt;/a&gt;) — two live defects from one design inconsistency, both cases of a string literal being resolved as a variable name when a variable happened to share its text. In a list literal (#29) the literal took its slot type tag from the colliding variable: colliding with a list gave a tag that led to a wild dereference and a segfault, and colliding with a number tagged a string pointer as an integer, so it printed as &lt;code&gt;4198536&lt;/code&gt; — silent wrong data, the worse half. Colliding with a &lt;code&gt;text&lt;/code&gt; was correct only by coincidence, the wrong tag and the right tag being the same number. In a buffer initialiser (#30), &lt;code&gt;a buffer called hello is &amp;quot;SURPRISE&amp;quot;.&lt;/code&gt; followed by &lt;code&gt;a buffer called b is &amp;quot;hello&amp;quot;.&lt;/code&gt; printed &lt;code&gt;SURPRISE&lt;/code&gt;: no crash, no diagnostic, just the wrong contents. Both belong to #19&amp;apos;s family, marked fixed in v0.4.4 — that fix removed the pattern from five codegen sites and missed these two. LANGUAGE.md&amp;apos;s grammar is unambiguous that a string literal is data, so this is a fix, not a change of meaning. The cure is narrow at each site: &lt;code&gt;tags.rs&lt;/code&gt; gains an &lt;code&gt;Expr::StringLit&lt;/code&gt; arm returning &lt;code&gt;TAG_STRING&lt;/code&gt; ahead of any lookup, leaving &lt;code&gt;Expr::Identifier&lt;/code&gt; alone, and &lt;code&gt;buffers.rs&lt;/code&gt; loses its &lt;code&gt;variable_types&lt;/code&gt; lookup entirely, the code beneath it already appending the literal&amp;apos;s bytes correctly. Eight regression tests covering every row of the matrix, controls included. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #29 and #30.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;text&lt;/code&gt; flag with no default reads as empty, not null&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#31&lt;/a&gt;) — &lt;code&gt;a flag called name is &amp;quot;-n&amp;quot; or &amp;quot;--name&amp;quot;, it is a text.&lt;/code&gt; segfaulted the moment the flag was read and the user had not supplied it. A flag&amp;apos;s slot was initialised to &lt;code&gt;0&lt;/code&gt; whatever its declared type, which is right for a &lt;code&gt;number&lt;/code&gt; or a &lt;code&gt;boolean&lt;/code&gt; and is a null pointer for a &lt;code&gt;text&lt;/code&gt;. A &lt;code&gt;text&lt;/code&gt; flag with no explicit default now initialises to the empty string, so an unsupplied flag reads as &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; and can be tested with &lt;code&gt;is empty&lt;/code&gt; the way the documented shape implies. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #31.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A flag keeps its declared type inside a function body&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#32&lt;/a&gt;) — a flag read inside a function was typed &lt;code&gt;boolean&lt;/code&gt; whatever it had been declared as, so a &lt;code&gt;text&lt;/code&gt; or &lt;code&gt;number&lt;/code&gt; flag compared or interpolated inside a function produced wrong code. The analyzer held declared flags in a bare &lt;code&gt;HashSet&amp;lt;String&amp;gt;&lt;/code&gt; — names, no types — and both type-query sites answered &lt;code&gt;Some(Type::Boolean)&lt;/code&gt; for any name in the set. It misbehaved only inside a function body, because at top level the declaration&amp;apos;s own type is still in scope and answers first, which is why the obvious one-line test passes and the defect could sit indefinitely. &lt;code&gt;flag_variables&lt;/code&gt; is now a &lt;code&gt;HashMap&amp;lt;String, Type&amp;gt;&lt;/code&gt;, populated from the declaration&amp;apos;s &lt;code&gt;value_type&lt;/code&gt;, and both query sites return the declared type. The regression test carries a top-level control alongside the function-body case to pin the diagnosis. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #32.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;is empty&lt;/code&gt; on a &lt;code&gt;text&lt;/code&gt; tests the contents, not the pointer&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#33&lt;/a&gt;) — &lt;code&gt;&amp;quot;&amp;quot; is empty&lt;/code&gt; was always false, for every &lt;code&gt;text&lt;/code&gt; in the language. The predicate special-cased buffers and lists (read the length field) and fell back to &lt;code&gt;test rax, rax&lt;/code&gt; for everything else; a text&amp;apos;s value is a pointer to its NUL-terminated bytes, which is never null, so the predicate compiled to &amp;quot;is this pointer null&amp;quot;. Found while verifying the documentation line #31&amp;apos;s fix earned — the claim that an unsupplied text flag can be tested with &lt;code&gt;is empty&lt;/code&gt; was written, then proven false before it shipped. The spec already promised the predicate on a text (its own worked example uses &lt;code&gt;if &amp;apos;output file&amp;apos; is empty then,&lt;/code&gt;), so this is a fix, not a feature. A text now tests its first byte, null-safely, at both twin codegen sites (expression and branch forms); both sites also stop resolving a string literal through &lt;code&gt;variable_types&lt;/code&gt; — the #19/#29 family pattern, removed. Not one test in the suite used &lt;code&gt;is empty&lt;/code&gt; before this bug&amp;apos;s regression pair. See &lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;docs/BUGS_FOUND.md&lt;/a&gt; #33.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.5</title>
    <id>tag:vox-lang.dev,2026:release/0.4.5</id>
    <updated>2026-08-19T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.5"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Loop expansion now honours its documented universality.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Loop expansion now honours its documented universality.&lt;/strong&gt; LANGUAGE.md promises that &lt;code&gt;each...from&lt;/code&gt; is a *universal* loop expansion that &amp;quot;works with any action,&amp;quot; yet an argument list holding more than one &lt;code&gt;each &amp;lt;name&amp;gt; from &amp;lt;collection&amp;gt;&lt;/code&gt; clause — or an expansion mixed with a fixed argument — was a parse error at the &lt;code&gt;and&lt;/code&gt;. That rejection was a gap in a promise the spec had already made, so this is a fix, not a new feature. A call&amp;apos;s argument list may now hold any number of &lt;code&gt;each &amp;lt;name&amp;gt; from &amp;lt;collection&amp;gt;&lt;/code&gt; clauses joined by &lt;code&gt;and&lt;/code&gt;, and the action runs once per element of the Cartesian product, row-major — the leftmost clause is the outermost loop, exactly as if the clauses were nested &lt;code&gt;For each&lt;/code&gt; loops. &lt;code&gt;&amp;apos;pair&amp;apos; of each x from [1, 2] and each y from [10, 20]&lt;/code&gt; calls &lt;code&gt;&amp;apos;pair&amp;apos;&lt;/code&gt; four times: &lt;code&gt;(1,10), (1,20), (2,10), (2,20)&lt;/code&gt;. There is no clause cap; a fixed (non-&lt;code&gt;each&lt;/code&gt;) argument may sit in any position; an inner collection may use an outer clause&amp;apos;s variable (triangle iteration). Arity is still checked — a one-value action with two &lt;code&gt;each&lt;/code&gt; clauses is a compile error, not a concatenation (`&lt;code&gt; &lt;/code&gt;print&lt;code&gt; takes one value but this sentence supplies 2 &lt;/code&gt;each&lt;code&gt; clauses. &lt;/code&gt;&lt;code&gt;), which is what keeps &lt;/code&gt;print each x from A and each y from B&lt;code&gt; from being misread. Duplicate loop variables in one sentence are a compile error naming the variable; an empty collection in any position yields zero calls; &lt;/code&gt;but if&lt;code&gt; attaches to the innermost iteration and may reference every loop variable; each loop variable retains its last-iteration value after the loop. The semantics is the Cartesian product (matching comprehension syntax in Haskell, Python, and Rust), not zip — &lt;/code&gt;respectively&lt;code&gt; is left as a possible future zip marker. A pure extension: today&amp;apos;s spellings of the form were all parse errors. Parser-only; the analyzer and codegen already handled nested &lt;/code&gt;For each` loops. See &lt;a href=&quot;docs/plans/320_grid_expansion.md&quot;&gt;docs/plans/320_grid_expansion.md&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.4</title>
    <id>tag:vox-lang.dev,2026:release/0.4.4</id>
    <updated>2026-08-18T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.4"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">The fuzzer&apos;s remaining findings, closed — the bug register is empty. Plus 4 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The fuzzer&amp;apos;s remaining findings, closed — the bug register is empty.&lt;/strong&gt; With 0.4.3&amp;apos;s two segfaults, every bug vox-fuzz has reported is now fixed, and so is the sibling that fixing #24 uncovered. - &lt;strong&gt;An integer literal too large for 64 bits compiled silently and evaluated to &lt;code&gt;0&lt;/code&gt;&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#22&lt;/a&gt;) — a wrong answer with no crash, which is the failure mode this manual says the language exists to prevent. It is now a compile-time error naming both the literal and the valid range. &lt;code&gt;9223372036854775807&lt;/code&gt; still compiles. - &lt;strong&gt;Printing a list of &lt;code&gt;arguments&amp;apos;s all&lt;/code&gt; leaked raw pointers&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#23&lt;/a&gt;) while &lt;code&gt;element N of&lt;/code&gt; read the same values back correctly — the payloads were sound, their type tags were not. Same family as #17/#18, fixed the same way. - &lt;strong&gt;Out-of-range positional properties segfaulted&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#26&lt;/a&gt;) — &lt;code&gt;arguments&amp;apos;s first&lt;/code&gt; with no arguments, &lt;code&gt;arguments&amp;apos;s second&lt;/code&gt; with fewer than two, &lt;code&gt;environment&amp;apos;s first&lt;/code&gt; and &lt;code&gt;last&lt;/code&gt; on an empty environment, and a negative index. Each handed a reader a null pointer to dereference. They now set the error flag and yield empty text, catchable by &lt;code&gt;On error&lt;/code&gt;, matching &lt;code&gt;last&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;all&lt;/code&gt;, &lt;code&gt;raw&lt;/code&gt;, and &lt;code&gt;count&lt;/code&gt;, which were already correct — the right behaviour had been implemented next door the whole time. - &lt;strong&gt;A string literal in a function-body &lt;code&gt;If&lt;/code&gt;/&lt;code&gt;While&lt;/code&gt; condition resolved as a variable name&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#21&lt;/a&gt;) — &lt;code&gt;If w is not &amp;quot;banana&amp;quot;&lt;/code&gt; failed with &lt;code&gt;Unknown variable: banana&lt;/code&gt;. This one was a &lt;strong&gt;regression&lt;/strong&gt;: an analyzer helper reintroduced the pre-0.3.0 quoted-token-as-identifier ambiguity that #19 removed from codegen, and it sat unreachable until an April cleanup widened a recursion guard and exposed it. The helper is deleted; a compile-fail test pins that genuine undeclared-identifier detection still works without it.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.3</title>
    <id>tag:vox-lang.dev,2026:release/0.4.3</id>
    <updated>2026-08-18T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.3"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Two segfaults, found by Vox&apos;s own fuzzer. Plus 10 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Two segfaults, found by Vox&amp;apos;s own fuzzer.&lt;/strong&gt; vox-fuzz — a fuzzer written in Vox — generated the programs that surfaced both, and both are now closed. - &lt;strong&gt;A declaration on a conditional path read uninitialised storage&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#25&lt;/a&gt;). A name declared inside an &lt;code&gt;On error&lt;/code&gt;, &lt;code&gt;While&lt;/code&gt;, &lt;code&gt;for each&lt;/code&gt;, or &lt;code&gt;Repeat&lt;/code&gt; body registered in the enclosing scope, but its initialising store sat behind the branch — so when the branch never ran, the name read a raw stack slot: a &lt;code&gt;number&lt;/code&gt; leaked a neighbouring frame&amp;apos;s values (one program printed &lt;code&gt;12345&lt;/code&gt; from an unrelated function, exit 0, no warning), and a &lt;code&gt;text&lt;/code&gt; was a wild pointer that segfaulted. The compiler now emits the type&amp;apos;s default at frame setup for any such name, so a declared name always holds its initializer or its type&amp;apos;s default, exactly as this manual has always promised. Programs where the branch *does* run are unaffected. - &lt;strong&gt;Reading an unset environment variable segfaulted&lt;/strong&gt; (&lt;a href=&quot;docs/BUGS_FOUND.md&quot;&gt;#24&lt;/a&gt;), and &lt;code&gt;On error&lt;/code&gt; could not catch it, because the fault preceded any error-flag write. A missing variable now sets the error flag and yields empty text, like every other fallible read.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Two diagnostics that pointed at the wrong thing.&lt;/strong&gt; The orphaned &lt;code&gt;Return&lt;/code&gt; error now carries a source location and explains that a body-level &lt;code&gt;Return&lt;/code&gt; closed the function early; the cross-condition &lt;code&gt;Unknown variable&lt;/code&gt; caret now lands on the failing read rather than on the declaration it was complaining about, with a hint naming the branch rule.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Nine more reserved words are now legal identifiers&lt;/strong&gt; — &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;raw&lt;/code&gt;, &lt;code&gt;all&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, &lt;code&gt;last&lt;/code&gt;, &lt;code&gt;second&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;length&lt;/code&gt;, and &lt;code&gt;version&lt;/code&gt;. Each was reserved as a keyword but is only special in one fixed grammatical position, the same contextual-keyword treatment that freed &lt;code&gt;count&lt;/code&gt; in 0.4.2. The compiler now lexes all nine as identifiers and claims each by lexeme at the position where it means something, leaving it an ordinary variable name everywhere else:&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;- &lt;code&gt;capacity&lt;/code&gt; — after a possessive marker (&lt;code&gt;data&amp;apos;s capacity&lt;/code&gt;) and in the &lt;code&gt;with capacity N&lt;/code&gt; / &lt;code&gt;of capacity N&lt;/code&gt; buffer-declaration phrase. - &lt;code&gt;raw&lt;/code&gt; — after a possessive marker (&lt;code&gt;the program&amp;apos;s raw&lt;/code&gt;). - &lt;code&gt;all&lt;/code&gt; — after a possessive marker (&lt;code&gt;the numbers&amp;apos;s all&lt;/code&gt;) and in the &lt;code&gt;all the numbers from/between … to and …&lt;/code&gt; range literal. - &lt;code&gt;first&lt;/code&gt;, &lt;code&gt;last&lt;/code&gt;, &lt;code&gt;second&lt;/code&gt; — after a possessive marker (&lt;code&gt;arguments&amp;apos;s first&lt;/code&gt;, &lt;code&gt;the letters&amp;apos;s last&lt;/code&gt;, &lt;code&gt;arguments&amp;apos;s second&lt;/code&gt;); &lt;code&gt;second&lt;/code&gt; also names the &lt;code&gt;Wait 1 second.&lt;/code&gt; time unit, so &lt;code&gt;Set second to 1. Wait second seconds.&lt;/code&gt; waits one second while &lt;code&gt;a number called second is 0.&lt;/code&gt; compiles. - &lt;code&gt;size&lt;/code&gt; and its synonym &lt;code&gt;length&lt;/code&gt; — after a possessive marker (&lt;code&gt;the letters&amp;apos;s size&lt;/code&gt;, &lt;code&gt;the letters&amp;apos;s length&lt;/code&gt;); &lt;code&gt;size&lt;/code&gt; also in the &lt;code&gt;with size N&lt;/code&gt; / &lt;code&gt;N bytes in size&lt;/code&gt; declaration phrases. - &lt;code&gt;version&lt;/code&gt; — the &lt;code&gt;Library &amp;lt;name&amp;gt; version &amp;quot;…&amp;quot;&lt;/code&gt; and &lt;code&gt;see &amp;lt;lib&amp;gt; version &amp;quot;…&amp;quot;&lt;/code&gt; header sentences only.&lt;/p&gt;&lt;p&gt;Each is a bare variable name everywhere except its one fixed grammatical position; &lt;code&gt;arguments&amp;apos;s first&lt;/code&gt; and &lt;code&gt;a number called first is 0.&lt;/code&gt; both work in the same program. The quoted forms (&lt;code&gt;&amp;apos;first&amp;apos;&lt;/code&gt;, &lt;code&gt;&amp;apos;size&amp;apos;&lt;/code&gt;, etc.), which always lexed identically to the bare forms, are unaffected. The reserved alias &lt;code&gt;length&lt;/code&gt; (previously an alternate spelling of &lt;code&gt;size&lt;/code&gt; in the alias table) is now a contextual keyword — a synonym of &lt;code&gt;size&lt;/code&gt; in the possessive dispatch only — so &lt;code&gt;a number called length is 1.&lt;/code&gt; compiles and &lt;code&gt;x&amp;apos;s length&lt;/code&gt; still means the same as &lt;code&gt;x&amp;apos;s size&lt;/code&gt;. See &lt;a href=&quot;docs/plans/315_contextual_keyword_family.md&quot;&gt;plan 315&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.2</title>
    <id>tag:vox-lang.dev,2026:release/0.4.2</id>
    <updated>2026-08-18T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.2"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Bare count is now a legal identifier. Plus 2 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Bare &lt;code&gt;count&lt;/code&gt; is now a legal identifier.&lt;/strong&gt; It was reserved as a keyword alongside &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;length&lt;/code&gt;, &lt;code&gt;first&lt;/code&gt;, and &lt;code&gt;last&lt;/code&gt;, but the word is only special after a possessive marker (&lt;code&gt;arguments&amp;apos;s count&lt;/code&gt;, &lt;code&gt;environment&amp;apos;s count&lt;/code&gt;) or in the &lt;code&gt;the argument count&lt;/code&gt; / &lt;code&gt;the environment variable count&lt;/code&gt; phrases. A word that is special in one syntactic position is no longer banned from every other, so &lt;code&gt;count&lt;/code&gt; is now an ordinary variable name — declarations, &lt;code&gt;Set&lt;/code&gt;, loop variables, function parameters, arithmetic, conditions — while every possessive &lt;code&gt;&amp;apos;s count&lt;/code&gt; use is unchanged. The compiler now lexes &lt;code&gt;count&lt;/code&gt; as an identifier and claims it for the possessive property in the parser, the same contextual-keyword treatment &lt;code&gt;start&lt;/code&gt;/&lt;code&gt;begin&lt;/code&gt;/&lt;code&gt;stop&lt;/code&gt; already get for timers. The quoted form &lt;code&gt;&amp;apos;count&amp;apos;&lt;/code&gt; (which always lexed identically to the bare form) is unaffected.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;cargo install vox-lang&lt;/code&gt; produced a compiler that could not compile anything.&lt;/strong&gt; Cargo copies only the binary into &lt;code&gt;~/.cargo/bin&lt;/code&gt;, leaving the crate&amp;apos;s &lt;code&gt;coreasm/&lt;/code&gt; behind in the registry cache, so every step of the resolution order missed and the first compile died with &lt;code&gt;unable to open include file &amp;apos;coreasm/x86_64/core.asm&amp;apos;&lt;/code&gt;. The crate already ships all 21 &lt;code&gt;.asm&lt;/code&gt; files — they are inside the crates.io tarball and covered by its checksum — so the compiler now carries them in the binary (a &lt;code&gt;build.rs&lt;/code&gt; walks &lt;code&gt;coreasm/&lt;/code&gt; at build time, so a newly added file ships without any list to update) and writes them to &lt;code&gt;~/.cache/vox/&amp;lt;version&amp;gt;/coreasm&lt;/code&gt; the first time it needs them. The tree is written to a temporary directory and renamed into place, so it is never observably half-written and two vox processes racing on first use is harmless. Nothing is downloaded, at build time or run time. The embedded copy is consulted &lt;strong&gt;last&lt;/strong&gt;, after &lt;code&gt;VOX_CORE_PATH&lt;/code&gt;, the XDG config, the system paths, the executable-relative search, and &lt;code&gt;./coreasm&lt;/code&gt; — so an RPM install, a development tree, and &lt;code&gt;VOX_CORE_PATH&lt;/code&gt; all behave exactly as before. See &lt;a href=&quot;docs/plans/312_cargo_install_coreasm.md&quot;&gt;plan 312&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Installing vox on dnf suggests vox-libs.&lt;/strong&gt; The RPM carries &lt;code&gt;Suggests: vox-libs&lt;/code&gt; — a weak dependency, so nothing is pulled in automatically and the compiler stays standalone, but the relationship is recorded where packaging tools can see it. README and INSTALL.md now document the libraries and where they install.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.1</title>
    <id>tag:vox-lang.dev,2026:release/0.4.1</id>
    <updated>2026-08-18T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.1"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Examples for everything 0.4.0 shipped. Plus 1 more change.</summary>
    <content type="html">&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Examples for everything 0.4.0 shipped.&lt;/strong&gt; &lt;code&gt;examples/delivery.vox&lt;/code&gt; (user- defined things end to end) and &lt;code&gt;examples/supervisor.vox&lt;/code&gt; (fork, non- blocking reap, deadline, Send signal, inline status decode) are new; &lt;code&gt;examples/pi.vox&lt;/code&gt; adopts &lt;code&gt;times&lt;/code&gt;; README&amp;apos;s feature list catches up.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The compiler ships no libraries.&lt;/strong&gt; &lt;code&gt;lib/process.vox&lt;/code&gt; moved out to &lt;a href=&quot;https://github.com/Vox-lang/vox-libs&quot;&gt;Vox-lang/vox-libs&lt;/a&gt;. The reaped- status tests decode inline, proving the feature is complete with nothing installed. The shared-library machinery tests are unchanged.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.4.0</title>
    <id>tag:vox-lang.dev,2026:release/0.4.0</id>
    <updated>2026-08-18T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.4.0"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">**Vox has a type system.** This is the biggest release in the language&apos;s history: as of today, a Vox program can define its own types — in plain English, like everything else here. Plus 6 more changes.</summary>
    <content type="html">&lt;p&gt;&lt;strong&gt;Vox has a type system.&lt;/strong&gt; This is the biggest release in the language&amp;apos;s history: as of today, a Vox program can define its own types — in plain English, like everything else here.&lt;/p&gt;&lt;p&gt;```vox A thing called point has a function called &amp;apos;placed at&amp;apos;, a number called x is 0, a number called y is 0.&lt;/p&gt;&lt;p&gt;To do the point&amp;apos;s &amp;apos;placed at&amp;apos;, with a number called across and a number called climb. a point called spot. Set spot&amp;apos;s x to across. Set spot&amp;apos;s y to climb. Return a point, spot.&lt;/p&gt;&lt;p&gt;The corner is a point&amp;apos;s &amp;apos;placed at&amp;apos; with 3 and 4. Print corner. ```&lt;/p&gt;&lt;p&gt;That prints &lt;code&gt;{x: 3, y: 4}&lt;/code&gt; — and yes, this example compiles; every example in this release does, checked against the compiler before shipping.&lt;/p&gt;&lt;p&gt;Things nest to any depth, copy by value, print themselves, compare field-by-field, carry their own function members, and work across files — all resolved at compile time, with not one byte of runtime added. The generated binaries are still just your code and syscalls.&lt;/p&gt;&lt;p&gt;And because a memory-safe language should have to prove it: this release was &lt;strong&gt;adversarially tested before it shipped&lt;/strong&gt;. A red team attacked the type system with 38 runnable probes; it found two real holes, both were fixed, and the exact programs that broke the compiler are now regression tests that must fail to break it. The copy semantics survived everything thrown at them.&lt;/p&gt;&lt;p&gt;Also in this release: Vox grows real process control — &lt;code&gt;Send signal&lt;/code&gt; performs &lt;code&gt;kill(2)&lt;/code&gt;, &lt;code&gt;reap ... without waiting&lt;/code&gt; polls without blocking, and &lt;code&gt;the reaped status&lt;/code&gt; finally tells you *how* a child died. Decoding it lives in &lt;code&gt;lib/process.vox&lt;/code&gt;, &lt;strong&gt;a library written in Vox, naturally&lt;/strong&gt; — not a standard library, and not something the compiler needs: &lt;code&gt;the reaped status&lt;/code&gt; hands you the raw word and any program may decode it itself. Vox has no standard library on purpose, and the compiler runs perfectly with none installed. A pure-Vox process supervisor (fork, poll, timeout, kill, classify) now needs no shell and no coreutils. Timers were caught reporting a 100 ms wait as a full second and now measure honestly, which matters rather a lot for the benchmarking tool this unblocks. And &lt;code&gt;Print 6 times 7.&lt;/code&gt; finally does what the manual always claimed.&lt;/p&gt;&lt;p&gt;The full ledger:&lt;/p&gt;&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;User-defined things — composite value types declared in the program.&lt;/strong&gt; A new &lt;code&gt;thing&lt;/code&gt; construct lets a program declare its own composite types at the top level, alongside the builtins. &lt;code&gt;A thing called point has a number called x is 0, a number called y is 0.&lt;/code&gt; declares a type &lt;code&gt;point&lt;/code&gt; with two number fields, each defaulted by a literal of its own type; the definition fixes a layout for the whole program and emits no code. A thing may also carry function members — &lt;code&gt;a function called &amp;apos;placed at&amp;apos;&lt;/code&gt; in the body declares the type&amp;apos;s callable surface (its manifest), defined separately with &lt;code&gt;To do the point&amp;apos;s &amp;apos;placed at&amp;apos;, with ...&lt;/code&gt;. Declarations bring a thing into being with &lt;code&gt;a point called origin.&lt;/code&gt; or &lt;code&gt;Create a point called p.&lt;/code&gt;; &lt;code&gt;the&lt;/code&gt; reads a known one. Fields are reached by possessive chains (&lt;code&gt;commute&amp;apos;s leg&amp;apos;s start&amp;apos;s x&lt;/code&gt;), and things nest (a segment holds two points) under an acyclicity check: a thing may name only previously- defined types, so a cycle is unconstructible within a file and is proved out across &lt;code&gt;see&lt;/code&gt;d files by the analyzer&amp;apos;s registry DFS. Things are values — assignment, argument passing, and return copy the whole thing field by field, so mutating a copy never touches the original. A member is called three ways: the free call &lt;code&gt;&amp;apos;magnitude squared&amp;apos; of origin&lt;/code&gt;, the instance possessive &lt;code&gt;origin&amp;apos;s &amp;apos;magnitude squared&amp;apos;&lt;/code&gt; (sugar that fills the first parameter with the receiver), and the type possessive &lt;code&gt;a point&amp;apos;s &amp;apos;placed at&amp;apos; with 3 and 4&lt;/code&gt; (a maker — the article &lt;code&gt;a&lt;/code&gt; because a new thing comes into being). A manifest member must return its own thing; its first parameter may be the thing (reachable by instance sugar) or not (a maker, reached by naming the type). Things print as &lt;code&gt;{x: 5, y: 0}&lt;/code&gt; and compare for equality field by field. Type, variable, and function names share one global identifier space (first-come-first-served); each type owns a separate member space. Cross-file, &lt;code&gt;see &amp;quot;./geometry.vox&amp;quot;.&lt;/code&gt; makes another file&amp;apos;s things usable — a &lt;code&gt;see&lt;/code&gt; of an unreadable file is now an error (it was silently skipped without &lt;code&gt;-v&lt;/code&gt;), and a duplicate type name across a &lt;code&gt;see&lt;/code&gt; now errors at the second definition, naming the other file. In v1 a field&amp;apos;s type is &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;, or any previously- defined thing (&lt;code&gt;text&lt;/code&gt;/&lt;code&gt;list&lt;/code&gt;/&lt;code&gt;map&lt;/code&gt;/&lt;code&gt;buffer&lt;/code&gt; deferred); user things are not part of the runtime tag system, so there is no &lt;code&gt;is a point&lt;/code&gt; predicate, and a &lt;code&gt;.lib&lt;/code&gt; cannot yet take or return a thing across its boundary (the diagnostic names the fields to pass across instead). Every reserved wrong-shape use has a targeted message — a thing copied from or written as a bare value, returned as a value, interpolated into text, or put in order; a member returning another thing, or declared but never defined; a maker reached by a receiver; a members-only or field-less definition; a definition written with &lt;code&gt;is&lt;/code&gt; instead of &lt;code&gt;has&lt;/code&gt;, created as a variable, defined inside a block, or defined after a variable of the same name; a field default of the wrong type; an unknown field type. &lt;code&gt;thing&lt;/code&gt;, &lt;code&gt;has&lt;/code&gt;, and &lt;code&gt;do&lt;/code&gt; are contextual keywords — claimed only inside the construct, ordinary identifiers elsewhere, so &lt;code&gt;a number called thing is 5.&lt;/code&gt; compiles. See the new &lt;a href=&quot;LANGUAGE.md#things&quot;&gt;Things&lt;/a&gt; chapter in LANGUAGE.md. Strictly additive: no existing program changes meaning; the construct, its diagnostics, the cross-file and &lt;code&gt;.lib&lt;/code&gt; refusals, and the &lt;code&gt;see&lt;/code&gt; behaviour tightenings are all new surface. Tests: &lt;code&gt;tests/330_thing_definition.vox&lt;/code&gt; through &lt;code&gt;tests/340_thing_see.vox&lt;/code&gt; plus &lt;code&gt;tests/include/geometry.vox&lt;/code&gt;, and the &lt;code&gt;tests/compile_fail/thing_*.err&lt;/code&gt; corpus.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Send signal &amp;lt;N&amp;gt; to process &amp;lt;pid&amp;gt;.&lt;/code&gt; performs &lt;code&gt;kill(2)&lt;/code&gt;.&lt;/strong&gt; A new statement that sends signal &lt;code&gt;&amp;lt;N-expr&amp;gt;&lt;/code&gt; to the process with PID &lt;code&gt;&amp;lt;pid-expr&amp;gt;&lt;/code&gt; (syscall 62). &lt;code&gt;child&lt;/code&gt; is accepted as an alias for &lt;code&gt;process&lt;/code&gt;, mirroring &lt;code&gt;reap process/child&lt;/code&gt;: &lt;code&gt;Send signal 9 to child pid.&lt;/code&gt;. On success it clears the error flag; on failure (&lt;code&gt;ESRCH&lt;/code&gt;, &lt;code&gt;EINVAL&lt;/code&gt;, &lt;code&gt;EPERM&lt;/code&gt;) it sets it, so &lt;code&gt;On error&lt;/code&gt; catches the failure exactly like the other syscall statements. Signal 0 is the standard no-deliver existence check, useful for probing the error path safely. Strictly additive: no existing program changes meaning.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;times&lt;/code&gt; is now a multiplication operator, an alias for &lt;code&gt;multiply&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;Print 6 times 7.&lt;/code&gt; and &lt;code&gt;Set n to n times 10.&lt;/code&gt; compile and behave exactly like their &lt;code&gt;multiply&lt;/code&gt; forms, including precedence — &lt;code&gt;Print 2 plus 3 times 4.&lt;/code&gt; evaluates to 14, multiplication still binding tighter than addition, identical to &lt;code&gt;2 plus 3 multiply 4.&lt;/code&gt;. &lt;code&gt;times&lt;/code&gt; was already a reserved keyword for the &lt;code&gt;Repeat &amp;lt;count&amp;gt; times,&lt;/code&gt; loop, so no lexer change was needed; the &lt;code&gt;Repeat&lt;/code&gt; count is read with &lt;code&gt;parse_primary&lt;/code&gt;, which never reaches the multiplicative layer, so the loop construct is unaffected. Strictly widening: no previously-valid program changes meaning.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;reap ... without waiting&lt;/code&gt; performs a non-blocking &lt;code&gt;wait4(2)&lt;/code&gt; (WNOHANG), and &lt;code&gt;the reaped status&lt;/code&gt; yields the raw wait-status word.&lt;/strong&gt; Any reap form (&lt;code&gt;reap any child process&lt;/code&gt;, &lt;code&gt;reap process &amp;lt;pid&amp;gt;&lt;/code&gt;, &lt;code&gt;reap child &amp;lt;pid&amp;gt;&lt;/code&gt;) takes a &lt;code&gt;without waiting&lt;/code&gt; suffix, which calls &lt;code&gt;wait4&lt;/code&gt; with &lt;code&gt;WNOHANG&lt;/code&gt; instead of blocking. The return value is the whole value of the form: the reaped child&amp;apos;s PID if one finished; &lt;code&gt;0&lt;/code&gt; if children exist but none has finished yet (this is &lt;strong&gt;not&lt;/strong&gt; an error — it is how &amp;quot;still running&amp;quot; is told from &amp;quot;gone&amp;quot;); or a negative value with the error flag set on a genuine error such as &lt;code&gt;ECHILD&lt;/code&gt; (no such child), catchable with &lt;code&gt;On error&lt;/code&gt;. A reap that returns &lt;code&gt;0&lt;/code&gt; reaps nothing and does not disturb &lt;code&gt;the reaped status&lt;/code&gt;. &lt;code&gt;the reaped status&lt;/code&gt; is a new expression yielding the raw &lt;code&gt;int status&lt;/code&gt; the kernel wrote, undecoded, from the most recent *successful* reap; before any reap it is &lt;code&gt;-1&lt;/code&gt; (a sentinel kept in &lt;code&gt;.data&lt;/code&gt;, not &lt;code&gt;.bss&lt;/code&gt;, so a &lt;code&gt;--shared&lt;/code&gt; library does not read &lt;code&gt;0&lt;/code&gt; and misreport &amp;quot;exited cleanly&amp;quot;). The compiler contains no knowledge of the wait-status encoding. &lt;code&gt;without&lt;/code&gt; is already a reserved keyword (the &lt;code&gt;print ... without newline&lt;/code&gt; token), so the suffix cannot be confused with a call argument, and &lt;code&gt;reaped&lt;/code&gt; / &lt;code&gt;waiting&lt;/code&gt; remain ordinary identifiers everywhere they are not these forms. Strictly additive: no existing program changes meaning.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;lib/process.vox&lt;/code&gt;, a library for decoding a wait status.&lt;/strong&gt; Ships in the repo as ordinary Vox and decodes the raw wait-status word with four functions matching the &lt;code&gt;&amp;lt;sys/wait.h&amp;gt;&lt;/code&gt; macros: &lt;code&gt;&amp;apos;exit code of&amp;apos;&lt;/code&gt; (bits 8–15), &lt;code&gt;&amp;apos;signal of&amp;apos;&lt;/code&gt; (the low 7 bits), &lt;code&gt;crashed&lt;/code&gt; (true if a signal killed it), and &lt;code&gt;&amp;apos;exited normally&amp;apos;&lt;/code&gt; (true if no signal was involved). Pulled in with &lt;code&gt;see &amp;quot;./lib/process.vox&amp;quot;.&lt;/code&gt;. Decoding lives here rather than in the compiler so that user-defined things (plan 310) can later wrap a status in a &lt;code&gt;process&lt;/code&gt; thing with no compiler change.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;This is a convenience library, not a standard library, and the compiler does not depend on it.&lt;/strong&gt; &lt;code&gt;the reaped status&lt;/code&gt; is complete on its own — it returns the raw word, and any program may decode it with &lt;code&gt;divide&lt;/code&gt;, &lt;code&gt;modulo&lt;/code&gt;, and &lt;code&gt;bit-and&lt;/code&gt; without &lt;code&gt;see&lt;/code&gt;ing anything. Vox deliberately has no standard library: the compiler must build and run with an empty &lt;code&gt;/usr/share/vox/lib/&lt;/code&gt;, and a language that assumed a package were installed would have a circular dependency it could never pay off. That directory is a *convention for users* — a place to drop your own libraries and reach them by bare name — not a compiler requirement. (An earlier draft of these notes called this file &amp;quot;the first standard-library file&amp;quot;. That was wrong on both counts and is corrected here.)&lt;/p&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Timer &lt;code&gt;duration&lt;/code&gt;/&lt;code&gt;elapsed&lt;/code&gt; reported the wrong time in every unit.&lt;/strong&gt; &lt;code&gt;... in milliseconds&lt;/code&gt; read whole seconds × 1000, so a 30 ms wait read 0 and a 1.5-second wait read 1000. The bare &lt;code&gt;the timer&amp;apos;s duration&lt;/code&gt; / &lt;code&gt;... elapsed&lt;/code&gt; forms and &lt;code&gt;... in seconds&lt;/code&gt; subtracted the monotonic clock&amp;apos;s *calendar second fields* (&lt;code&gt;end_sec − start_sec&lt;/code&gt;) instead of the real elapsed time, so a 100 ms wait that straddled a second boundary read 1 — a tenfold error — and a 1500 ms wait read 1 or 2 depending on where it began. &lt;code&gt;Start&lt;/code&gt; and &lt;code&gt;Stop&lt;/code&gt; already captured the nanosecond halves into &lt;code&gt;TIMER_START_MONO_NSEC&lt;/code&gt; / &lt;code&gt;TIMER_END_MONO_NSEC&lt;/code&gt;; nothing ever read them. A new internal &lt;code&gt;TIMER_ELAPSED_NANOSECONDS&lt;/code&gt; helper now subtracts the full timespec with borrow handling (the shape &lt;code&gt;TIME_ELAPSED_PRECISE&lt;/code&gt; already used), handles both the stored-end path (timer stopped) and the still-running path (sampling &lt;code&gt;clock_gettime&lt;/code&gt; into a stack timespec and reading &lt;code&gt;[rsp + 8]&lt;/code&gt; for nanoseconds), and leaves a 128-bit nanosecond total in &lt;code&gt;rdx:rax&lt;/code&gt;. &lt;code&gt;TIMER_DURATION_SECONDS&lt;/code&gt; and &lt;code&gt;TIMER_DURATION_MILLISECONDS&lt;/code&gt; share that helper and differ only in the divisor (&lt;code&gt;NANOSECONDS_PER_SECOND&lt;/code&gt; vs &lt;code&gt;NANOSECONDS_PER_MILLISECOND&lt;/code&gt;), so seconds is now true truncation of the real elapsed time and milliseconds is true milliseconds. The meaning of the seconds/bare forms is unchanged — still whole truncated seconds, never milliseconds — but their values are now correct and no longer depend on where the interval fell within a second. This unblocks the planned Vox benchmarking tool, which is useless when a sub-second run reads zero. Regression tests: &lt;code&gt;tests/350_timer_subsecond_milliseconds.vox&lt;/code&gt;, &lt;code&gt;tests/351_timer_millisecond_boundary.vox&lt;/code&gt;, &lt;code&gt;tests/352_timer_seconds_still_whole.vox&lt;/code&gt;, &lt;code&gt;tests/353_timer_elapsed_while_running.vox&lt;/code&gt;, &lt;code&gt;tests/354_timer_bare_duration_whole_seconds.vox&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A reserved word used as a loop variable reported &amp;quot;Missing loop variable&amp;quot; instead of naming the reserved word.&lt;/strong&gt; &lt;code&gt;print each arg from argv.&lt;/code&gt; failed with &amp;quot;Missing loop variable after &amp;apos;each&amp;apos;&amp;quot; even though the variable was not missing — it was &lt;code&gt;arg&lt;/code&gt;, which the lexer folds onto &lt;code&gt;Token::Argument&lt;/code&gt; (an alias of the reserved keyword &lt;code&gt;argument&lt;/code&gt;). Because the parser saw a keyword token where it expected an identifier, it reported the variable as absent and sent the reader hunting for a syntax error that did not exist. Both each-loop variable sites (&lt;code&gt;each &amp;lt;var&amp;gt; from ...&lt;/code&gt; and &lt;code&gt;for each &amp;lt;var&amp;gt; from ...&lt;/code&gt;) now delegate to the existing &lt;code&gt;check_not_keyword&lt;/code&gt; diagnostic when the token in the variable slot is a keyword, so the message names the spelling the user actually typed and notes that &lt;code&gt;arg&lt;/code&gt; is an alternate spelling of &lt;code&gt;argument&lt;/code&gt;. A genuinely omitted variable is still reported as &amp;quot;Missing loop variable&amp;quot;: the loop&amp;apos;s own &lt;code&gt;from&lt;/code&gt;/&lt;code&gt;between&lt;/code&gt; delimiters are excluded from the keyword check, so &lt;code&gt;print each from argv.&lt;/code&gt; keeps its existing message. Diagnostics only — the program is still rejected, just with an accurate reason. No words were un-reserved and no loop semantics changed. Regression tests: &lt;code&gt;tests/compile_fail/087_reserved_word_each_loop_variable.vox&lt;/code&gt;, &lt;code&gt;tests/compile_fail/088_reserved_word_for_each_loop_variable.vox&lt;/code&gt;, &lt;code&gt;tests/compile_fail/089_missing_loop_variable_keyword_delim.vox&lt;/code&gt;, and &lt;code&gt;tests/322_each_loop_reserved_word_regression.vox&lt;/code&gt; (the renamed form still compiles and iterates).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.7</title>
    <id>tag:vox-lang.dev,2026:release/0.3.7</id>
    <updated>2026-08-16T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.7"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">begin, stop, and finish are no longer reserved words. Plus 9 more changes.</summary>
    <content type="html">&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;begin&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt;, and &lt;code&gt;finish&lt;/code&gt; are no longer reserved words.&lt;/strong&gt; They now behave exactly like &lt;code&gt;start&lt;/code&gt; always did: the parser claims them for a timer statement only when a name operand follows (&lt;code&gt;Start the t.&lt;/code&gt;, &lt;code&gt;stop t.&lt;/code&gt;), and everywhere else they are ordinary identifiers — &lt;code&gt;a number called stop is 0.&lt;/code&gt; now compiles instead of being rejected as a reserved keyword. The timer dispatch also gained that one-token lookahead for all four words, so a program can define and call its own zero-argument &lt;code&gt;start.&lt;/code&gt;/&lt;code&gt;stop.&lt;/code&gt; function; previously a bare &lt;code&gt;start.&lt;/code&gt; was swallowed by the timer parser and died with &amp;quot;Expected timer name&amp;quot;. Strictly widening: no previously-valid program changes meaning.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The compiler source is reorganised into focused modules.&lt;/strong&gt; Each compilation phase was a single very large &lt;code&gt;mod.rs&lt;/code&gt; — codegen 11,061 lines, parser 7,224, analyzer 4,032, lexer 1,091 — which made the code hard to navigate, review, and contribute to. Every phase is now a directory of topical modules (for example &lt;code&gt;codegen/expr.rs&lt;/code&gt;, &lt;code&gt;codegen/tags.rs&lt;/code&gt;, &lt;code&gt;parser/control_flow.rs&lt;/code&gt;, &lt;code&gt;analyzer/scope.rs&lt;/code&gt;), with &lt;code&gt;mod.rs&lt;/code&gt; reduced to the phase&amp;apos;s type, shared constants, and module declarations: 494, 205, 200, and 52 lines respectively. This is &lt;strong&gt;pure code motion — no behaviour change&lt;/strong&gt;. Every step was verified by compiling the whole example and test corpus and confirming the emitted assembly stayed byte-identical to the pre-refactor compiler&amp;apos;s, alongside the full test suite. Nothing about the language, the CLI, or any public interface changes; the difference is purely that the source is now navigable.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Appending a format string to a list stored a corrupt element&lt;/strong&gt; (BUGS_FOUND #17). &lt;code&gt;append &amp;quot;fmt {x}&amp;quot; to out.&lt;/code&gt; — and a &lt;code&gt;text&lt;/code&gt; local initialized from a format string and appended by name — wrote the element&amp;apos;s runtime type tag as plain integer instead of text, because neither the pre-scan nor the emit-time tag selector recognised &lt;code&gt;Expr::FormatString&lt;/code&gt; as always producing text. Reading the corrupted element (whole-list print, &lt;code&gt;element N of&lt;/code&gt;, or &lt;code&gt;for each&lt;/code&gt;) then reinterpreted a valid string pointer as an integer: sometimes a raw pointer address printed in place of the text, sometimes a crash, depending on what surrounding code did with the misread value. Fixed by teaching both the pre-scan (&lt;code&gt;prescan_expr_tag&lt;/code&gt;) and the emit-time fallback (&lt;code&gt;infer_expr_type&lt;/code&gt;) that a format string is always &lt;code&gt;text&lt;/code&gt;. Regression tests: &lt;code&gt;tests/bugs_found_17_format_append_text.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_17_format_append_number.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_17_format_append_buffer.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_17_format_append_named.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_17_element_access.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_17_for_each.vox&lt;/code&gt;, plus three codegen unit tests pinning the tag write and the no-spurious- widening behaviour.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The &lt;code&gt;.lib&lt;/code&gt; table of contents under-reported list element types for provably-&lt;code&gt;text&lt;/code&gt; elements&lt;/strong&gt; (BUGS_FOUND #18). A &lt;code&gt;--shared&lt;/code&gt; build&amp;apos;s element- type scan credited only a direct literal or a parameter&amp;apos;s declared type, so a &lt;code&gt;text&lt;/code&gt; local&amp;apos;s declared type, a called function&amp;apos;s declared &lt;code&gt;text&lt;/code&gt; return, and a format-string append (once #17 made the element itself sound) all shipped as plain &lt;code&gt;list&lt;/code&gt; instead of &lt;code&gt;list of text&lt;/code&gt;, even though the runtime tagger already agreed on &lt;code&gt;text&lt;/code&gt; and consumers already printed correctly. The scan now credits all three. A genuinely mixed or evidence-free list is unaffected — still plain &lt;code&gt;list&lt;/code&gt;. Regression tests: &lt;code&gt;plan_303_local_declared_type_credits_element_parameter&lt;/code&gt;, &lt;code&gt;plan_303_call_declared_return_type_credits_element_parameter&lt;/code&gt;, &lt;code&gt;plan_303_format_string_credits_element_parameter&lt;/code&gt;, &lt;code&gt;plan_303_newly_credited_shapes_in_return_position&lt;/code&gt;, &lt;code&gt;plan_303_function_call_return_type_scoped_per_library&lt;/code&gt;, &lt;code&gt;plan_303_local_declared_type_conflict_stays_unknown&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A string literal&amp;apos;s content was silently resolved against known variable (or top-level constant) names at codegen time&lt;/strong&gt; (BUGS_FOUND #19). The crash form: &lt;code&gt;a text called x is &amp;quot;x&amp;quot;.&lt;/code&gt; reads &lt;code&gt;x&lt;/code&gt;&amp;apos;s own not-yet-written slot instead of the literal (its declared type is registered before its initializer is generated), segfaulting on first use. The much wider, silent form: &lt;code&gt;a text called greeting is &amp;quot;hello&amp;quot;. a text called b is &amp;quot;greeting&amp;quot;. Print b.&lt;/code&gt; printed &lt;code&gt;hello&lt;/code&gt;, not &lt;code&gt;greeting&lt;/code&gt; — any literal whose text coincides with any in-scope variable&amp;apos;s name, in an initializer or a bare &lt;code&gt;Print &amp;quot;literal&amp;quot;.&lt;/code&gt;, silently took that variable&amp;apos;s value instead, and a literal matching a &lt;code&gt;float&lt;/code&gt;/&lt;code&gt;buffer&lt;/code&gt; variable&amp;apos;s name could flip an &lt;code&gt;is a&lt;/code&gt; type predicate or an equality comparison&amp;apos;s codegen strategy. Every &lt;code&gt;Expr::StringLit&lt;/code&gt; codegen site now treats its payload as text unconditionally, with no variable-table or constant-table lookup on its content — matching LANGUAGE.md&amp;apos;s post-0.3.0 rule that a double-quoted token is data everywhere, never a name. Identifier-based resolution (bare and single-quoted names, map lookups, &lt;code&gt;{name}&lt;/code&gt; format-string interpolation) is unchanged. Regression tests: &lt;code&gt;tests/bugs_found_19_self_name_initializer.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_19_other_name_initializer.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_19_other_name_print_direct.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_19_predicate.vox&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Comparing a &lt;code&gt;text&lt;/code&gt;/&lt;code&gt;buffer&lt;/code&gt;/string literal to a &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, or &lt;code&gt;map&lt;/code&gt; for equality dereferenced the non-stringy operand as a string pointer&lt;/strong&gt; (BUGS_FOUND #20). &lt;code&gt;If &amp;quot;abc&amp;quot; is equal to 3.5 then, ...&lt;/code&gt; segfaulted with no variable or name collision involved at all; &lt;code&gt;list&lt;/code&gt;/&lt;code&gt;map&lt;/code&gt; operands didn&amp;apos;t crash but gave a wrong answer via a suspected out-of-bounds read. Pre-existing, but the #19 fix made it commonly reachable: a literal that happens to share a variable&amp;apos;s name (e.g. &lt;code&gt;&amp;quot;pi&amp;quot; is equal to pi&lt;/code&gt;) previously took a different, wrong-but-non-crashing path by accident, and now correctly reaches this one. Comparing a stringy operand against a *provably* non-stringy one now folds to a compile-time constant (&lt;code&gt;is equal to&lt;/code&gt; → false, &lt;code&gt;is not equal to&lt;/code&gt; → true) without evaluating either operand; &lt;code&gt;text&lt;/code&gt;/&lt;code&gt;buffer&lt;/code&gt; comparisons, and comparisons involving a dynamic &lt;code&gt;value&lt;/code&gt; operand, are unaffected. Regression tests: &lt;code&gt;tests/bugs_found_20_no_collision.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_20_float_collision.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_20_number_boolean_list.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_20_not_equal.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_20_buffer_text_positive.vox&lt;/code&gt;, &lt;code&gt;tests/bugs_found_20_return_position.vox&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;End&lt;/code&gt; is no longer documented as a timer-stop spelling.&lt;/strong&gt; It never worked: &lt;code&gt;end&lt;/code&gt; lexes into the &lt;code&gt;exit&lt;/code&gt; keyword family, so &lt;code&gt;End the t.&lt;/code&gt; was a parse error despite LANGUAGE.md listing it beside &lt;code&gt;Stop&lt;/code&gt;/&lt;code&gt;Finish&lt;/code&gt;. The spelling list now matches the compiler.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Documentation&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Documented how to close more than one level of nesting.&lt;/strong&gt; A period closes one open clause and a blank line closes every open clause, but nothing described the space between them: periods stack, so N periods close N levels. This is also how an author chooses which &lt;code&gt;if&lt;/code&gt; an &lt;code&gt;Otherwise&lt;/code&gt; or &lt;code&gt;But if&lt;/code&gt; continues — an else-chain continues the innermost &lt;code&gt;if&lt;/code&gt; still open, so closing that &lt;code&gt;if&lt;/code&gt; first hands the branch to the enclosing one, a one-character difference in the source. Undocumented, this was easy to get wrong in a way that produces no error: too few periods and following statements are absorbed into a clause the author believed was closed, and if one of them is a loop&amp;apos;s increment the program hangs silently. LANGUAGE.md gains a *Closing more than one level* section with worked examples at one, two and three periods, the equivalent empty &lt;code&gt;Otherwise,.&lt;/code&gt; form, and &lt;code&gt;tests/nested_clause_close_levels.vox&lt;/code&gt; pins the behaviour. No compiler change: the parser was correct throughout.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &amp;quot;Projects built with Vox&amp;quot; section in the README.&lt;/strong&gt; Lists actively developed FOSS projects written in Vox, with an invitation to add yours by emailing vox-lang@tegosec.com.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A design document and implementation plan for the module split&lt;/strong&gt; (&lt;code&gt;docs/MODULE_SPLIT_DESIGN.md&lt;/code&gt;, &lt;code&gt;docs/plans/306_module_split.md&lt;/code&gt;), recording the strategy and the procedure the refactor below followed.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.6</title>
    <id>tag:vox-lang.dev,2026:release/0.3.6</id>
    <updated>2026-08-14T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.6"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">but if is now a general conditional branch. Plus 20 more changes.</summary>
    <content type="html">&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;but if&lt;/code&gt; is now a general conditional branch.&lt;/strong&gt; Both the default action and each branch action may be any valid statement, in the plain form and in loop expansion — previously only &lt;code&gt;print&lt;/code&gt; (and, outside loop expansion, &lt;code&gt;append&lt;/code&gt;) could carry a &lt;code&gt;but if&lt;/code&gt;, and anything else was rejected with &amp;quot;&amp;apos;but if&amp;apos; conditional branching only works with print statements&amp;quot;. A branch body is parsed with the ordinary statement parser rather than a per-statement-kind grammar, so new statement kinds gain &lt;code&gt;but if&lt;/code&gt; support automatically. The terse &lt;code&gt;append &amp;lt;value&amp;gt;&lt;/code&gt; form, which inherits its target from the base statement, still works, and a branch naming a different list or buffer than the base is still a compile error.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;In-place retyping for &lt;code&gt;value&lt;/code&gt; variables.&lt;/strong&gt; The statement &lt;code&gt;&amp;lt;valuevar&amp;gt; is a &amp;lt;type&amp;gt;.&lt;/code&gt; converts a &lt;code&gt;value&lt;/code&gt; variable in place and updates its runtime tag. Supported targets are &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;/&lt;code&gt;decimal&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, and &lt;code&gt;boolean&lt;/code&gt;; the conversion follows the same rules as the static cast table. The same phrase in condition position (&lt;code&gt;If v is a number then, ...&lt;/code&gt;) remains a type predicate. A failed runtime conversion sets &lt;code&gt;_last_error&lt;/code&gt; and leaves the variable holding &lt;code&gt;0&lt;/code&gt; so &lt;code&gt;On error&lt;/code&gt; can catch it; retyping a statically-typed variable is a compile error with a remedy pointing at the explicit cast.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A warning when a function is still open at end of file.&lt;/strong&gt; A function body is closed by a blank line, so without one the rest of the file is read as part of the body and the program silently does nothing. The compiler now points at the function definition instead of compiling a do-nothing binary in silence. Suppressed for &lt;code&gt;Library&lt;/code&gt; files and &lt;code&gt;--shared&lt;/code&gt; builds, where a trailing function ending at EOF is correct by construction. This is a diagnostic only — the parsing behaviour is unchanged.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;type&lt;/code&gt; property on every variable.&lt;/strong&gt; &lt;code&gt;&amp;lt;var&amp;gt;&amp;apos;s type&lt;/code&gt; returns a text description of the variable&amp;apos;s declared type, e.g. &lt;code&gt;Number (static)&lt;/code&gt; for a &lt;code&gt;number&lt;/code&gt; or &lt;code&gt;Text (dynamic)&lt;/code&gt; for a &lt;code&gt;value&lt;/code&gt;. Statically-typed variables fold to a compile-time literal; &lt;code&gt;value&lt;/code&gt; variables dispatch on the runtime tag already kept in their shadow slot or BSS mirror. Intended for debugging and logging — type tests still use the &lt;code&gt;is a &amp;lt;type&amp;gt;&lt;/code&gt; predicate.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Seven compiler bugs found while building a JSON library&lt;/strong&gt;, all with regression tests: - A &lt;code&gt;float&lt;/code&gt; interpolated into a &lt;code&gt;text&lt;/code&gt;/&lt;code&gt;buffer&lt;/code&gt; destination (&lt;code&gt;a text called t is &amp;quot;{y}&amp;quot;.&lt;/code&gt;) printed the raw IEEE-754 bit pattern instead of the number. - &lt;code&gt;buffer as text&lt;/code&gt; returned the buffer&amp;apos;s struct pointer rather than its character data, so the cast silently produced an empty string. - Extracting a &lt;code&gt;float&lt;/code&gt; from a &lt;code&gt;value&lt;/code&gt; by reassignment (&lt;code&gt;the y is v.&lt;/code&gt; / &lt;code&gt;Set y to v.&lt;/code&gt;) produced the raw bit pattern; only declaration with an initializer worked. A &lt;code&gt;value&lt;/code&gt; source no longer overwrites the destination&amp;apos;s declared type. - Extracting a &lt;code&gt;list&lt;/code&gt; from a &lt;code&gt;value&lt;/code&gt; produced a bogus pointer and a length of &lt;code&gt;-1&lt;/code&gt;, while the same extraction into a &lt;code&gt;float&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt; worked. - &lt;strong&gt;Assignments to a top-level variable inside a function did not persist, and could read another function&amp;apos;s local.&lt;/strong&gt; Top-level &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;buffer&lt;/code&gt;, and &lt;code&gt;value&lt;/code&gt; variables now live in one storage location shared by top-level code and every function, so a write inside a function is visible after it returns. Previously such a write allocated an uninitialised per-call stack slot, so a counter could read an unrelated function&amp;apos;s local instead of its own value. For &lt;code&gt;value&lt;/code&gt; specifically, its runtime type tag is stored alongside the payload in its own shared location too, kept paired with it on every read and write, so the value keeps behaving as the type it currently holds — not just an integer that happens to round-trip. Declaring a variable of the same name inside a function still shadows the global, and recursion still gets per-call locals. - A map key taken from &lt;code&gt;map&amp;apos;s keys&lt;/code&gt; never matched on lookup, always returning the not-found sentinel, even though the key printed correctly. - Chaining an index over a property read (&lt;code&gt;element 1 of m&amp;apos;s values&lt;/code&gt;) produced garbage; the same read split across two statements was correct.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;but if&lt;/code&gt; chain was closed by a period belonging to a nested clause, so every later branch was silently lost.&lt;/strong&gt; A period closes only the innermost open clause, but a &lt;code&gt;but if&lt;/code&gt; branch consumed one that belonged to a clause *inside* it — most visibly an &lt;code&gt;On error&lt;/code&gt; handler attached to a fallible action in the branch. A dispatch loop giving each branch its own failure handling ran only its first branch, with no error; the same structure without &lt;code&gt;On error&lt;/code&gt; produced a misattributed &lt;code&gt;Unknown variable&lt;/code&gt; error pointing at an unrelated, valid line. A branch body is now parsed as a block, like an &lt;code&gt;If&lt;/code&gt; branch, so it can hold its own trailing clause, and a period followed by &lt;code&gt;but&lt;/code&gt; continues the chain instead of ending it. A period that genuinely ends the chain still ends it.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Reassigning a &lt;code&gt;value&lt;/code&gt; that held a &lt;code&gt;float&lt;/code&gt; to an integer left the static type stale at &lt;code&gt;float&lt;/code&gt;.&lt;/strong&gt; The runtime tag was written correctly, but declaring &lt;code&gt;a value called v is 3.5.&lt;/code&gt; let the initializer&amp;apos;s type-inference demote the &lt;code&gt;value&lt;/code&gt; from its &lt;code&gt;Mixed&lt;/code&gt; (runtime-tagged) type to a concrete &lt;code&gt;Float&lt;/code&gt;, so every later read dispatched on the stale static type instead of the tag: &lt;code&gt;Print v&lt;/code&gt; emitted &lt;code&gt;PRINT_FLOAT&lt;/code&gt; and reinterpreted the integer &lt;code&gt;1&lt;/code&gt; as the denormal &lt;code&gt;0.0&lt;/code&gt;, and &lt;code&gt;If v is a number&lt;/code&gt; folded statically to false. A declared &lt;code&gt;value&lt;/code&gt; now keeps &lt;code&gt;Mixed&lt;/code&gt; through its initializer — the same guard the bare-assignment arm already had — so reads dispatch on the runtime tag as intended. Covers all three assignment spellings (&lt;code&gt;Set v to&lt;/code&gt;, &lt;code&gt;the v is&lt;/code&gt;, &lt;code&gt;v is&lt;/code&gt;) and a function reassigning a top-level &lt;code&gt;value&lt;/code&gt; global.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A declared-but-uninitialized &lt;code&gt;text&lt;/code&gt; variable held a null pointer, so printing, interpolating, or comparing it segfaulted the process on the first read&lt;/strong&gt; (&lt;code&gt;a text called ex.&lt;/code&gt; / &lt;code&gt;Create a text called ex.&lt;/code&gt;, then &lt;code&gt;Print ex.&lt;/code&gt;). Every other default-initializing type had a real, safe default; &lt;code&gt;text&lt;/code&gt; fell through to a generic zero-fill that later reads dereferenced. An uninitialized &lt;code&gt;text&lt;/code&gt; now points at a real, shared empty string, so it reads, prints, and interpolates as &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt; and can be reassigned normally afterward.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Create a TYPE called NAME.&lt;/code&gt; (and the bare &lt;code&gt;a TYPE called NAME.&lt;/code&gt; form with no initializer) now default-initializes every declarable type uniformly&lt;/strong&gt;, routed through one shared type resolver instead of a hardcoded subset. Previously only &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, and &lt;code&gt;buffer&lt;/code&gt; default-initialized this way; &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt;, and &lt;code&gt;timer&lt;/code&gt; now do too. &lt;code&gt;file&lt;/code&gt; and &lt;code&gt;time&lt;/code&gt; still require an explicit initializer — a default value would be meaningless for either (no path to open, no timestamp to hold) — and are rejected at compile time with a message naming what to supply. - An uninitialized &lt;code&gt;value&lt;/code&gt; now defaults to &lt;code&gt;nothing&lt;/code&gt;, not the number &lt;code&gt;0&lt;/code&gt;. - An empty &lt;code&gt;map&lt;/code&gt; now prints &lt;code&gt;{}&lt;/code&gt;, not &lt;code&gt;{&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A reserved-word error now names the word you wrote.&lt;/strong&gt; Declaring a variable called &lt;code&gt;length&lt;/code&gt; reported that &lt;code&gt;&amp;apos;size&amp;apos;&lt;/code&gt; was reserved, because the lexer canonicalises the alias before the check runs. It now reads &amp;quot;Cannot use &amp;apos;length&amp;apos; as a variable name&amp;quot; and explains that &lt;code&gt;length&lt;/code&gt; is an alternate spelling of &lt;code&gt;size&lt;/code&gt;. &lt;code&gt;length&lt;/code&gt;/&lt;code&gt;size&lt;/code&gt; is also documented in the reserved-alias table.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;An unmatched &lt;code&gt;{&lt;/code&gt; in a string literal has a real diagnostic.&lt;/strong&gt; It previously failed with an empty-named &lt;code&gt;Unknown variable: &lt;/code&gt;; it now names the unmatched brace and points at &lt;code&gt;{{&lt;/code&gt; / &lt;code&gt;}}&lt;/code&gt; as the literal-brace escape.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;LANGUAGE.md&amp;apos;s blank-line rule corrected.&lt;/strong&gt; It claimed a blank line after a function definition was &amp;quot;a style convention, not a requirement&amp;quot;. A blank line is in fact the only thing that closes a function body.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.5</title>
    <id>tag:vox-lang.dev,2026:release/0.3.5</id>
    <updated>2026-08-11T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.5"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Nine compiler bugs found while building a JSON library. Plus 7 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Nine compiler bugs found while building a JSON library&lt;/strong&gt;, all with regression tests: - &lt;code&gt;Print&lt;/code&gt; on an inlined float-returning call no longer prints the raw bit-pattern. - &lt;code&gt;Return a boolean, A and B.&lt;/code&gt; now parses and evaluates correctly even inside an &lt;code&gt;If&lt;/code&gt; branch. - A nested, self-terminated &lt;code&gt;If ... .&lt;/code&gt; no longer closes the outer statement early (both &lt;code&gt;If&lt;/code&gt; branches and &lt;code&gt;While&lt;/code&gt; bodies). - A function call in an arithmetic expression now binds tighter than the surrounding operator instead of absorbing it as an argument. - Same-named locals in different functions no longer corrupt each other&amp;apos;s list/element-type inference. - Reading an element (or iterating with &lt;code&gt;For each&lt;/code&gt;) from a bare &lt;code&gt;list&lt;/code&gt; parameter now preserves the per-slot runtime type tag. - &lt;code&gt;{{&lt;/code&gt; and &lt;code&gt;}}&lt;/code&gt; in string literals now collapse to literal &lt;code&gt;{&lt;/code&gt; and &lt;code&gt;}&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.4</title>
    <id>tag:vox-lang.dev,2026:release/0.3.4</id>
    <updated>2026-08-09T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.4"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A but if branch on a non-print action (e.g. append) could silently discard the rest of the program. Plus 2 more changes.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;but if&lt;/code&gt; branch on a non-&lt;code&gt;print&lt;/code&gt; action (e.g. &lt;code&gt;append&lt;/code&gt;) could silently discard the rest of the program.&lt;/strong&gt; The branch&amp;apos;s grammar didn&amp;apos;t consume a trailing &lt;code&gt;to &amp;lt;name&amp;gt;&lt;/code&gt; clause, which desynced the parser into treating everything after it as the body of a bogus, never-called function. This was a regression: the previous release rejected the same source with a compile error instead of silently discarding it. It&amp;apos;s a compile error again if the branch names the wrong target, and consumed correctly otherwise.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A function could only declare a handful of parameter and return types.&lt;/strong&gt; &lt;code&gt;number&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;buffer&lt;/code&gt;, &lt;code&gt;file&lt;/code&gt;, &lt;code&gt;time&lt;/code&gt;, &lt;code&gt;timer&lt;/code&gt;, and &lt;code&gt;value&lt;/code&gt; now all work identically as a parameter type and a declared return type, for ordinary functions and for shared-library (&lt;code&gt;.lib&lt;/code&gt;) functions alike. Previously only five of these were accepted as a return type at all, and &lt;code&gt;float&lt;/code&gt;/&lt;code&gt;time&lt;/code&gt;/&lt;code&gt;timer&lt;/code&gt; weren&amp;apos;t accepted anywhere.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A list returned or passed across a &lt;code&gt;.lib&lt;/code&gt; boundary printed raw memory addresses instead of its actual contents.&lt;/strong&gt; The list&amp;apos;s length and structure always crossed correctly; only its element type was lost. The compiler now infers a list&amp;apos;s element type from the exporting function&amp;apos;s own code and carries it across the boundary automatically, for every call shape - no new syntax required.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.3</title>
    <id>tag:vox-lang.dev,2026:release/0.3.3</id>
    <updated>2026-08-08T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.3"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">Reassigning a variable to a value of a different type could silently produce a wrong number, or segfault.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Reassigning a variable to a value of a different type could silently produce a wrong number, or segfault&lt;/strong&gt; — the compiler&amp;apos;s tracked type for a variable could disagree with what the variable actually held at runtime, and formatting/printing code trusted the tracked type. Depending on the direction of the mismatch this either printed a pointer address as if it were a number, or dereferenced a raw number as if it were a string pointer and crashed:&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;``&lt;code&gt; a number called n is 5. n is &amp;quot;abc&amp;quot;. Print &amp;quot;{n}&amp;quot;.        -&amp;gt; printed a garbage number; could also segfault depending on which way the mismatch ran &lt;/code&gt;``&lt;/p&gt;&lt;p&gt;A variable&amp;apos;s type is now fixed at its declaration and never changes. A write that would change it — &lt;code&gt;n is &amp;quot;abc&amp;quot;.&lt;/code&gt;, &lt;code&gt;the n is &amp;quot;abc&amp;quot;.&lt;/code&gt;, or &lt;code&gt;Set n to &amp;quot;abc&amp;quot;.&lt;/code&gt;, and reusing an already-declared name as a loop variable, an &lt;code&gt;open ... called&lt;/code&gt; target, or an &lt;code&gt;Allocate ... for&lt;/code&gt; target — is now a compile error instead:&lt;/p&gt;&lt;p&gt;``&lt;code&gt; n is &amp;quot;abc&amp;quot;.   -&amp;gt; error: cannot assign text to &amp;apos;n&amp;apos;, which is a number help: convert it explicitly:  n is &amp;quot;abc&amp;quot; as a number. &lt;/code&gt;``&lt;/p&gt;&lt;p&gt;&lt;strong&gt;If a program you have relies on this&lt;/strong&gt;, convert the value explicitly with &lt;code&gt;as a number&lt;/code&gt; / &lt;code&gt;as text&lt;/code&gt; / &lt;code&gt;as a float&lt;/code&gt; / &lt;code&gt;as a boolean&lt;/code&gt; at the point of assignment — this syntax already existed and is unchanged. A variable declared &lt;code&gt;a value called x&lt;/code&gt; is unaffected and keeps accepting any type, as documented.&lt;/p&gt;&lt;p&gt;This also closes several related cases with the same root cause: incrementing or decrementing a text variable, a declaration inside an untaken &lt;code&gt;If&lt;/code&gt;/&lt;code&gt;Otherwise&lt;/code&gt;/&lt;code&gt;While&lt;/code&gt;/&lt;code&gt;Repeat&lt;/code&gt;/&lt;code&gt;for&lt;/code&gt; branch or an &lt;code&gt;on error&lt;/code&gt; handler that never fires, a nested declaration that reuses an outer variable&amp;apos;s name with a different type, and reading a mismatched value out of a map whose value type is provable from its own literal.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.2</title>
    <id>tag:vox-lang.dev,2026:release/0.3.2</id>
    <updated>2026-08-07T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.2"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A function call inside an explicit {...} group failed to parse when the enclosing statement had reserved of or to for itself.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A function call inside an explicit &lt;code&gt;{...}&lt;/code&gt; group failed to parse when the enclosing statement had reserved &lt;code&gt;of&lt;/code&gt; or &lt;code&gt;to&lt;/code&gt; for itself&lt;/strong&gt; — most visibly, &lt;code&gt;byte {&amp;lt;call&amp;gt;} of &amp;lt;buffer&amp;gt;&lt;/code&gt; and &lt;code&gt;element {&amp;lt;call&amp;gt;} of &amp;lt;list&amp;gt;&lt;/code&gt; rejected any function call in the braces, even a single-argument one, so a program had to precompute the index into a local variable first instead of writing it directly.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;``&lt;code&gt; byte {ci of 1 and 2} of b     -&amp;gt; error: Expected a statement, got And &lt;/code&gt;``&lt;/p&gt;&lt;p&gt;The connector-precedence fix in 0.3.0 reserved &lt;code&gt;of&lt;/code&gt;/&lt;code&gt;to&lt;/code&gt; for the duration of parsing an index or bound, so an identifier immediately followed by that word couldn&amp;apos;t swallow the enclosing statement&amp;apos;s own connector. But the reservation wasn&amp;apos;t cleared when parsing entered an explicit &lt;code&gt;{...}&lt;/code&gt; group — even though the closing brace already unambiguously ends the group, leaving nothing left to protect. It now correctly parses:&lt;/p&gt;&lt;p&gt;``&lt;code&gt; byte {ci of 1 and 2} of b     -- compiles and evaluates correctly &lt;/code&gt;``&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.1</title>
    <id>tag:vox-lang.dev,2026:release/0.3.1</id>
    <updated>2026-08-07T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.1"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A .lib&apos;s declared return type silently dropped to void.</summary>
    <content type="html">&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;.lib&lt;/code&gt;&amp;apos;s declared return type silently dropped to void&lt;/strong&gt; for any function whose &lt;code&gt;Return&lt;/code&gt; was not its first statement — the common case for any function with real logic before returning. &lt;code&gt;Return&lt;/code&gt;&amp;apos;s type is parsed by two different code paths depending on where it sits in the function body; only one of them fed the parsed type back into the function&amp;apos;s declared return type. A library&amp;apos;s own interface file could describe a function as returning nothing when it genuinely returned a value.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;``&lt;code&gt; To gb with a number called x. a number called y is x add x. Return a number, y. &lt;/code&gt;``&lt;/p&gt;&lt;p&gt;Before this fix, the emitted &lt;code&gt;.lib&lt;/code&gt; read &lt;code&gt;To gb with a number called x.&lt;/code&gt; — no &lt;code&gt;, returning a number&lt;/code&gt; clause. It now correctly reads &lt;code&gt;To gb with a number called x, returning a number.&lt;/code&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.3.0</title>
    <id>tag:vox-lang.dev,2026:release/0.3.0</id>
    <updated>2026-08-07T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.3.0"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">&quot;...&quot; is now always a string literal — never an identifier. Plus 7 more changes.</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;&amp;quot;...&amp;quot;&lt;/code&gt; is now always a string literal — never an identifier. Names are bare words (&lt;code&gt;total&lt;/code&gt;), or &lt;code&gt;&amp;apos;single quoted&amp;apos;&lt;/code&gt; when they contain spaces (&lt;code&gt;&amp;apos;total items&amp;apos;&lt;/code&gt;). This closes the single overload that has caused this project&amp;apos;s worst defects: &lt;code&gt;a number called &amp;quot;x&amp;quot; is &amp;quot;get five&amp;quot;.&lt;/code&gt; used to silently parse a function call as a string and print a stray pointer instead of calling anything. The &amp;quot;Names and strings&amp;quot; section of &lt;code&gt;LANGUAGE.md&lt;/code&gt; is the full guide.&lt;/p&gt;&lt;p&gt;&amp;gt; &lt;strong&gt;Breaking.&lt;/strong&gt; Every existing &lt;code&gt;.vox&lt;/code&gt; program that names anything the old way &amp;gt; (&lt;code&gt;a number called &amp;quot;x&amp;quot; is 5.&lt;/code&gt;, &lt;code&gt;To &amp;quot;greet&amp;quot;.&lt;/code&gt;, &lt;code&gt;print &amp;quot;greet&amp;quot; of 3.&lt;/code&gt;, &amp;gt; &lt;code&gt;Library &amp;quot;lib&amp;quot; version &amp;quot;1.0&amp;quot;.&lt;/code&gt;) now fails to compile, with a diagnostic &amp;gt; telling you the correct replacement. There is no compatibility window. &amp;gt; &amp;gt; | Before | After | &amp;gt; |---|---| &amp;gt; | &lt;code&gt;a number called &amp;quot;x&amp;quot; is 5.&lt;/code&gt; | &lt;code&gt;a number called x is 5.&lt;/code&gt; | &amp;gt; | &lt;code&gt;a number called &amp;quot;total items&amp;quot; is 5.&lt;/code&gt; | &lt;code&gt;a number called &amp;apos;total items&amp;apos; is 5.&lt;/code&gt; | &amp;gt; | &lt;code&gt;To &amp;quot;greet&amp;quot; with a number called &amp;quot;n&amp;quot;.&lt;/code&gt; | &lt;code&gt;To greet with a number called n.&lt;/code&gt; | &amp;gt; | &lt;code&gt;print &amp;quot;greet&amp;quot; of 3.&lt;/code&gt; | &lt;code&gt;print greet of 3.&lt;/code&gt; | &amp;gt; | &lt;code&gt;Library &amp;quot;mathkit&amp;quot; version &amp;quot;1.0&amp;quot;.&lt;/code&gt; | &lt;code&gt;Library mathkit version &amp;quot;1.0&amp;quot;.&lt;/code&gt; | &amp;gt; &amp;gt; &lt;strong&gt;Unchanged, still double-quoted&lt;/strong&gt; — these were never names: map keys &amp;gt; (&lt;code&gt;person&amp;apos;s &amp;quot;name&amp;quot;&lt;/code&gt;), file/library paths (&lt;code&gt;see &amp;quot;./utils.vox&amp;quot;&lt;/code&gt;, &amp;gt; &lt;code&gt;from &amp;quot;./lib.lib&amp;quot;&lt;/code&gt;), flag aliases (&lt;code&gt;&amp;quot;-v&amp;quot;&lt;/code&gt;), and version strings &amp;gt; (&lt;code&gt;version &amp;quot;1.0&amp;quot;&lt;/code&gt;). &amp;gt; &amp;gt; A mechanical migration tool ships in this repo at &amp;gt; &lt;code&gt;tools/migrate-identifiers&lt;/code&gt;; it rewrote this project&amp;apos;s own 250+ file test &amp;gt; corpus and is a reasonable starting point for a large program, though its &amp;gt; output should be reviewed.&lt;/p&gt;&lt;h3&gt;Fixed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;A function call could silently misparse as a string literal&lt;/strong&gt;, printing a raw pointer instead of calling anything — &lt;code&gt;a number called x is &amp;quot;get five&amp;quot;.&lt;/code&gt; compiled and ran, printing something like &lt;code&gt;4198480&lt;/code&gt;. The old grammar (&lt;code&gt;name ::= string | identifier&lt;/code&gt;) made this possible in any position a name was expected; it no longer exists.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;The same defect shape, independently, in &lt;code&gt;element N of&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;element 1 of &amp;quot;no such thing&amp;quot;.&lt;/code&gt; compiled and printed &lt;code&gt;0&lt;/code&gt; — a string naming nothing was silently treated as an out-of-bounds access rather than rejected. Bare string literals are now rejected in this position with a clear diagnostic.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A value-typed parameter&amp;apos;s runtime type tag could leak across function definitions.&lt;/strong&gt; If two functions in the same file reused a parameter name (e.g. both taking a parameter called &lt;code&gt;x&lt;/code&gt;), a later string literal that happened to equal that name could be misread as a stale value tag instead of literal data. &lt;code&gt;variable_types&lt;/code&gt;/&lt;code&gt;mixed_tag_slots&lt;/code&gt; are now scoped per function, matching how ordinary variables already were.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A &lt;code&gt;.lib&lt;/code&gt;&amp;apos;s declared return type was silently dropped to void&lt;/strong&gt; for any function whose &lt;code&gt;Return&lt;/code&gt; was not its first statement — the common case for any function with real logic before returning. The library&amp;apos;s own interface file described a function as returning nothing when it returned a value.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;to&lt;/code&gt;/&lt;code&gt;of&lt;/code&gt;/&lt;code&gt;with&lt;/code&gt; as universal call connectors collided with grammar that already used those words&lt;/strong&gt; — &lt;code&gt;Set x to 1.&lt;/code&gt; followed later by &lt;code&gt;x&lt;/code&gt; used as a range bound, &lt;code&gt;append ... to&lt;/code&gt;, and &lt;code&gt;element N of&lt;/code&gt;/&lt;code&gt;byte N of&lt;/code&gt; with a variable index could all misparse as function calls, consuming a token that belonged to the enclosing statement.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;docs/check-samples.sh&lt;/code&gt;&lt;/strong&gt; — extracts every runnable code sample from &lt;code&gt;LANGUAGE.md&lt;/code&gt;, compiles it against the built compiler, and reports honest pass/fail/skip counts with an internal consistency check (&lt;code&gt;checked + skipped&lt;/code&gt; must equal the real number of samples). Every sample in the language reference is now verified to actually compile, not merely asserted to.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;A C-interoperability test&lt;/strong&gt; confirming a Vox &lt;code&gt;--shared&lt;/code&gt; library is genuinely callable from C: a built &lt;code&gt;.so&lt;/code&gt; has zero &lt;code&gt;NEEDED&lt;/code&gt; entries (freestanding), exports the documented mangled symbol names, and a C driver linked against it produces the exact expected output.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;tools/migrate-identifiers&lt;/code&gt;&lt;/strong&gt; — the mechanical migration tool described above, with its own test suite (idempotent, byte-identical on already-canonical input, preserves the semantic meaning of blank lines between function definitions).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Vox 0.2.0</title>
    <id>tag:vox-lang.dev,2026:release/0.2.0</id>
    <updated>2026-08-03T00:00:00Z</updated>
    <link rel="alternate" type="text/html" href="https://github.com/Vox-lang/vox/releases/tag/v0.2.0"/>
    <category term="Voxlang"/>
    <category term="ProgrammingLanguages"/>
    <category term="Compilers"/>
    <summary type="text">A Vox program can now call a Vox library. Plus 6 more changes.</summary>
    <content type="html">&lt;p&gt;A Vox program can now call a Vox library. Build a library with &lt;code&gt;--shared&lt;/code&gt;, then consume it from another Vox program with &lt;code&gt;see &amp;quot;&amp;lt;lib&amp;gt;&amp;quot; version &amp;quot;&amp;lt;ver&amp;gt;&amp;quot; from &amp;quot;&amp;lt;path&amp;gt;.lib&amp;quot;.&lt;/code&gt;. The &amp;quot;Shared libraries&amp;quot; section of &lt;code&gt;LANGUAGE.md&lt;/code&gt; is the full guide.&lt;/p&gt;&lt;p&gt;&amp;gt; &lt;strong&gt;Breaking.&lt;/strong&gt; This release breaks three things a non-Vox consumer, a &amp;gt; &lt;code&gt;--shared&lt;/code&gt; build, or a stale &lt;code&gt;see&lt;/code&gt; can depend on. Each is detailed under &amp;gt; &lt;code&gt;### Removed&lt;/code&gt; and &lt;code&gt;### Changed&lt;/code&gt; below; the summary: &amp;gt; &amp;gt; 1. &lt;strong&gt;Every exported library symbol is renamed&lt;/strong&gt; to &lt;code&gt;&amp;lt;lib&amp;gt;_&amp;lt;version&amp;gt;_&amp;lt;func&amp;gt;&lt;/code&gt; &amp;gt;    (e.g. &lt;code&gt;add_two_numbers&lt;/code&gt; → &lt;code&gt;mathkit_1_0_add_two_numbers&lt;/code&gt;), with no &amp;gt;    unmangled alias. Any C/Rust/assembly consumer must update its &lt;code&gt;extern&lt;/code&gt; &amp;gt;    declarations and relink. &amp;gt; 2. &lt;strong&gt;&lt;code&gt;--shared&lt;/code&gt; now requires a &lt;code&gt;Library&lt;/code&gt; declaration.&lt;/strong&gt; Add &amp;gt;    &lt;code&gt;Library &amp;quot;name&amp;quot; version &amp;quot;x.y&amp;quot;.&lt;/code&gt; at the top of the library source. &amp;gt; 3. &lt;strong&gt;Three &lt;code&gt;see&lt;/code&gt; forms that silently linked nothing are now compile &amp;gt;    errors.&lt;/strong&gt; Switch to &lt;code&gt;see &amp;quot;&amp;lt;lib&amp;gt;&amp;quot; version &amp;quot;&amp;lt;ver&amp;gt;&amp;quot; from &amp;quot;&amp;lt;path&amp;gt;.lib&amp;quot;&lt;/code&gt;. &amp;gt; &amp;gt; Upgrading from before 0.1.23? Two earlier breaking changes are documented &amp;gt; under their own releases below: arithmetic on a text/buffer/list/file now &amp;gt; errors with a cast suggestion (&lt;code&gt;0.1.21&lt;/code&gt;), and &lt;code&gt;.en&lt;/code&gt; source includes no &amp;gt; longer inline — use &lt;code&gt;.vox&lt;/code&gt; (&lt;code&gt;0.1.23&lt;/code&gt;).&lt;/p&gt;&lt;h3&gt;Removed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Three &lt;code&gt;see&lt;/code&gt; forms that silently linked nothing are now compile errors.&lt;/strong&gt; &lt;code&gt;see &amp;quot;./path.so&amp;quot;.&lt;/code&gt;, &lt;code&gt;see &amp;quot;lib&amp;quot; version &amp;quot;1.0&amp;quot; from &amp;quot;./path.so&amp;quot;.&lt;/code&gt;, and &lt;code&gt;see &amp;quot;./path.so&amp;quot; for &amp;quot;lib&amp;quot; version &amp;quot;1.0&amp;quot;.&lt;/code&gt; previously compiled while linking nothing — every call into the library was simply missing, with no warning. They now error and name the canonical form &lt;code&gt;see &amp;quot;&amp;lt;lib&amp;gt;&amp;quot; version &amp;quot;&amp;lt;ver&amp;gt;&amp;quot; from &amp;quot;&amp;lt;path&amp;gt;.lib&amp;quot;.&lt;/code&gt; (the &lt;code&gt;see ... for ...&lt;/code&gt; form has its own diagnostic). A previously *silent* failure is now loud — that is the point of the change, not a regression. If a build breaks here, build the library with &lt;code&gt;--shared&lt;/code&gt; (which writes the &lt;code&gt;.lib&lt;/code&gt; beside the &lt;code&gt;.so&lt;/code&gt;) and point &lt;code&gt;see&lt;/code&gt; at the &lt;code&gt;.lib&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Changed&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Every exported library symbol is renamed.&lt;/strong&gt; Labels are now &lt;code&gt;&amp;lt;library&amp;gt;_&amp;lt;version&amp;gt;_&amp;lt;function&amp;gt;&lt;/code&gt; — for example &lt;code&gt;add_two_numbers&lt;/code&gt; becomes &lt;code&gt;mathkit_1_0_add_two_numbers&lt;/code&gt;, and &lt;code&gt;greet&lt;/code&gt; becomes &lt;code&gt;mathkit_1_0_greet&lt;/code&gt;. There is deliberately &lt;strong&gt;no unmangled alias&lt;/strong&gt;: an alias would let two versions of one library collide in the same &lt;code&gt;.so&lt;/code&gt;, which is exactly what the scheme exists to prevent.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you call a Vox library from C, Rust, or assembly, update your &lt;code&gt;extern&lt;/code&gt; declarations and relink:&lt;/p&gt;&lt;p&gt;``&lt;code&gt;nasm ; before extern add_two_numbers extern greet ; after extern mathkit_1_0_add_two_numbers extern mathkit_1_0_greet &lt;/code&gt;``&lt;/p&gt;&lt;p&gt;Find the new names for any library you have with:&lt;/p&gt;&lt;p&gt;``&lt;code&gt;bash $ nm -D --defined-only libmathkit.so 00000000000005c4 T mathkit_1_0_add_two_numbers 00000000000005f9 T mathkit_1_0_greet &lt;/code&gt;``&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;--shared&lt;/code&gt; now requires a &lt;code&gt;Library&lt;/code&gt; declaration.&lt;/strong&gt; A &lt;code&gt;--shared&lt;/code&gt; build with no &lt;code&gt;Library&lt;/code&gt; line has no identity to mangle with and no &lt;code&gt;.lib&lt;/code&gt; to emit, and now errors:&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;``&lt;code&gt; error: A shared library must declare its identity with a &lt;/code&gt;Library&lt;code&gt; declaration giving its name and version — without one there is no mangling and no &lt;/code&gt;.lib&lt;code&gt;. Add &lt;/code&gt;Library &amp;quot;name&amp;quot; version &amp;quot;x.y&amp;quot;.&lt;code&gt; before the function definitions and rebuild with --shared. &lt;/code&gt;``&lt;/p&gt;&lt;p&gt;Add &lt;code&gt;Library &amp;quot;name&amp;quot; version &amp;quot;x.y&amp;quot;.&lt;/code&gt; at the top of the library source.&lt;/p&gt;&lt;h3&gt;Added&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Consuming a library from Vox.&lt;/strong&gt; &lt;code&gt;see &amp;quot;&amp;lt;lib&amp;gt;&amp;quot; version &amp;quot;&amp;lt;ver&amp;gt;&amp;quot; from &amp;quot;&amp;lt;path&amp;gt;.lib&amp;quot;.&lt;/code&gt; resolves the &lt;code&gt;.lib&lt;/code&gt;, selects the block matching name *and* version, verifies every promised symbol against the &lt;code&gt;.so&lt;/code&gt;&amp;apos;s dynamic symbol table (a stale &lt;code&gt;.lib&lt;/code&gt; is a compile error, not a runtime crash), registers the signatures so calls type-check, and links the &lt;code&gt;.so&lt;/code&gt; with an &lt;code&gt;-rpath&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;```vox see &amp;quot;mathkit&amp;quot; version &amp;quot;1.0&amp;quot; from &amp;quot;./libmathkit.lib&amp;quot;.&lt;/p&gt;&lt;p&gt;a number called &amp;quot;sum&amp;quot; is &amp;quot;add two numbers&amp;quot; of 3 and 4. Print the sum. ```&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;The &lt;code&gt;.lib&lt;/code&gt; interface file.&lt;/strong&gt; A &lt;code&gt;--shared&lt;/code&gt; build writes &lt;code&gt;&amp;lt;output&amp;gt;.lib&lt;/code&gt; beside the &lt;code&gt;.so&lt;/code&gt; — the library&amp;apos;s name and version, the &lt;code&gt;Location&lt;/code&gt; of its &lt;code&gt;.so&lt;/code&gt;, and a table of contents of every exported function&amp;apos;s signature. It is the only place Vox types live; a &lt;code&gt;.so&lt;/code&gt; carries mangled names but no types.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;``` Library &amp;quot;mathkit&amp;quot; version &amp;quot;1.0&amp;quot;. Location &amp;quot;./libmathkit.so&amp;quot;.&lt;/p&gt;&lt;p&gt;Table of Contents: To &amp;quot;add two numbers&amp;quot; with a number called &amp;quot;a&amp;quot; and a number called &amp;quot;b&amp;quot;, returning a number. To &amp;quot;greet&amp;quot;. ```&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Several libraries — and several versions of one library — in one &lt;code&gt;.so&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;vox a.vox b.vox --shared -o lib.so&lt;/code&gt; links multiple libraries in a single link step (you cannot append to a linked &lt;code&gt;.so&lt;/code&gt;). Two versions of the same library coexist with distinct mangled symbols, so a consumer can keep calling &lt;code&gt;mathkit_1_0_add_two_numbers&lt;/code&gt; after &lt;code&gt;mathkit_2_0_add_two_numbers&lt;/code&gt; ships beside it, with no recompile. Duplicate &lt;code&gt;&amp;lt;library, version&amp;gt;&lt;/code&gt; pairs across inputs are rejected; multi-input is &lt;code&gt;--shared&lt;/code&gt; only.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Consumption diagnostics.&lt;/strong&gt; Each failure mode is its own error naming the file and what was expected: a missing &lt;code&gt;.lib&lt;/code&gt;; no such library in it (with the libraries it does declare); a version mismatch (listing the versions offered); a missing &lt;code&gt;.so&lt;/code&gt; at &lt;code&gt;Location&lt;/code&gt;; a stale &lt;code&gt;.lib&lt;/code&gt; promising a symbol the &lt;code&gt;.so&lt;/code&gt; does not export (naming the mangled symbol); and an arity or type mismatch at the call site (naming the library and version).&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
</feed>
