You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
[0.6.0] - 2026-07-18
Changed
BREAKING: {} is now always the empty set; the empty map is {:}. {} in
a map-typed position no longer resolves to an empty map - it is a compile error
that points at {:}, and the reverse ({:} where a set is expected) reports
the same way. Migration is mechanical: map<K,V> m = {} becomes map<K,V> m = {:}. Set literals are unaffected. This supersedes the contextual {} resolution added in 0.5.0: Type::EmptySetOrMap and its span-keyed
override map are removed, so the set/map ambiguity no longer reaches semantics
or codegen. Closes #266.
Valgrind and benchmark PR reports render as charts: the PR comment now shows
a leak split pie, per-phase median bars, and an exact-numbers table instead of
raw log dumps; raw logs stay in the collapsed details. All rendered values are
whitelisted or numeric-validated since Build artifacts are fork-controlled (#267).
CI fails on orphaned insta snapshots: deleting or renaming a test_scripts/
file no longer strands its snapshot as phantom coverage; 52 accumulated orphans
were deleted and the gate keeps new ones out (#271).
Added
if expressions support else if chains and multi-line branches: an if
used in expression position (auto g = if c { a } else { b }) previously
accepted only a single-line, single-else form. It now accepts else if
chains and branches whose value spans multiple lines, matching the statement
form. Each branch is still a single value expression; a chain parses as a
nested if-expression, so no new AST shape, semantics, or codegen was needed
(branch type-agreement checks still apply). Closes #281.
Fixed
++/-- on a captured variable inside a closure: a standalone count++
in a lambda body was rejected by the parser's no-postfix-in-expression guard as
if it were nested in an expression, even though a bare x++ statement is valid
anywhere. The guard now only rejects a postfix ++/-- genuinely nested inside
a larger expression (e.g. a + y++), which stays disallowed by design. Codegen
already handled the captured increment. Closes #280.
Match-arm bindings no longer leak past the match: a match-arm pattern
binding (e.g. n in n if n % 2 == 0) or an arm-body local stayed in the
codegen variable table after the match, so a later declaration reusing the
name (auto n = 3) reused the arm-local slot. That slot's alloca lived in a
conditional arm block that did not dominate the later store, so the program
failed with invalid LLVM IR ("instruction does not dominate all uses"). Match
bindings are now scoped to the match; a subsequent same-named declaration gets
a fresh slot. Reassignment of an outer variable inside an arm still persists.
Recursive and mutually-recursive functions in imported modules: a call to
a function in the same imported module (including a recursive self-call) was
emitted against the bare, unmangled LLVM name and failed with "Undefined
function: ". The current-function name recorded while generating an
imported module body is now the mangled module!name, so same-module calls
resolve through the existing nested-name logic. A related failure - a
non-identifier argument such as n - 1 in a recursive call reporting
"Undefined variable" - is fixed by resolving argument types through the
codegen fallback tables when the analyzer's function scope is unavailable.
Cross-module constant access: reading a const defined in an imported
module through the module namespace (math.PI) now compiles. Previously only
stdlib module constants resolved; a user module constant fell through to
"Field access not supported for expression type Identifier". The module's own
code could already read the constant; only the namespaced access from an
importing file was missing. A same-named local in the caller does not shadow
the module constant. (Known limitation: two imported modules that declare the
same-named constant collide under the flat global-name model, tracked in #279.)
Compound assignment and increment on class fields: self.value++, self.value += n, and the obj.field forms now compile. Previously ++/--
on a field failed codegen with "Cannot increment on non-identifier" and +=/-=
with "Assignment to non-identifier/deref not implemented", even though semantic
analysis accepted them. Both now route through the same field-store path as a
plain obj.field = ... assignment, so reference counting and where-clause field
invariants still apply.
auto now rejects nested empty collections consistently: auto x = {1: []}
and auto x = [[]] previously passed semantic analysis with an unresolved
element type, while the set/map equivalents were caught. All empty collection
kinds are now guarded.
Empty-collection inference help text: suggested {} for every collection,
including lists (list<int> myVar = {}). Each kind now shows its own literal
syntax ([], {:}, {}).
Security
Lockfile bump for the postgres stack: postgres-protocol 0.6.11 -> 0.6.12
(RUSTSEC-2026-0179 SCRAM CPU-exhaustion DoS, RUSTSEC-2026-0180 hstore decode
panic) and tokio-postgres 0.7.17 -> 0.7.18 (RUSTSEC-2026-0178 short DataRow
panic); postgres-types 0.2.13 -> 0.2.14 moved with the stack (no advisory of
its own). Transitive via mux-runtime's postgres dependency; cargo audit is
clean again.