0.8.82
Breaking
substring(s, start, end)now takes an exclusive end index, not a length.
The freesubstring(...)builtin previously treated its third argument as a
length, while the.substring(...)method, thes[start:end]slice
operator,list.slice,bytes_slice, and the language spec all use an
exclusive end index. The builtin now matches that single convention, so
substring("hello world", 6, 9)returns"wor"and the two call forms agree.
Both forms share one implementation, so they can no longer drift. Migrate any
length-style calls: a "last N chars"substring(s, len(s) - n, n)becomes
substring(s, len(s) - n)(omit the end to run to the string end), and a
fixed-length slicesubstring(s, i, n)becomessubstring(s, i, i + n).
Added
- Experimental
harn-codegencrate: a Cranelift-backed native compiler for
Harn's scalar-compute subset. Lowersint/float/boolfunctions
(arithmetic, comparisons, logical ops, branches, loops, locals) from VM
bytecode to native machine code, with an in-process JIT (NativeFunction),
an object-file backend (emit_object), a pure-Rust reference interpreter,
and aharn-nativecCLI. It ispublish = falseand is not a dependency of
harn-cli/harn-vm, so the distributed binary never links Cranelift; build
it explicitly with-p harn-codegen. See
docs/src/dev/native-codegen.md. harn pg codegennow formats its rendered output, so generated type files
areharn fmt-clean by construction.harn lintandharn checkskip style and unused-declaration lints for
machine-generated*.generated.harnfiles (type diagnostics still apply, and
harn fmtstill formats them). The signal is the filename, not an in-file
@generated/DO NOT EDITcomment, so a generated marker cannot be pasted in
to silence lints on a hand-written file.std/postgres/queryprojection helpers (uuid_text,timestamptz_json,
nullable_timestamptz_json) now accept table-qualified column names such as
timestamptz_json("vaults.created_at"). Each dot-separated segment is
validated as an identifier and the output alias is the trailing segment
(created_at), so projections from joined queries compose through
columns([...])withoutunsafe_sql(...)or brace escaping.std/postgres/queryprojection helpers (uuid_text,timestamptz_json,
nullable_timestamptz_json,select_clause) now return trusted
PgSqlFragments, and a newcolumns(parts)helper joins projection
fragments/strings into one{projection}fragment. Column projections now
drop intosql(...)placeholders without anunsafe_sql(...)wrapper, and
carry the literal'{}'JSON path safely.
Fixed
hostlib_code_index_*line_countno longer overcounts files that end in a newline.
The code-index file scanner counted lines withcontent.split('\n').count(),
which reports one phantom extra line for any file with a trailing newline (the
common case) — e.g. a two-line file ending in\nwas reported as 3 lines.
Line counting is now shared through a singlecount_lineshelper, matching the
scanner and process-artifact surfaces that already counted correctly, so the
line_countfield surfaced to scripts is accurate.- Conformance fixture
.harn/dirs now ignore every runtime SQLite DB tests drop in (e.g. the rate-limit
storellm-rate-limits.sqlite), not just the event log, so a stray DB no longer trips the release guard. - Fixed unsound typed-opcode specialization for reassignable bindings. The
compiler trusted avar/for-item binding's initializer-inferred primitive
type when emitting typed fast-path opcodes (AddInt,LessInt, …), even
though such a binding can be reassigned through anany-typed value of a
different runtime primitive. Because typed opcodes hard-error on an operand
type mismatch, the optimized build could throw a spurious
Typed int add expected int operands, got int and floaton a program the
unoptimized build runs correctly. The compiler now keeps the typed fast path
only for bindings a new monomorphism analysis can prove keep a single
primitive type across their initializer and every reassignment in scope; all
others fall back to the generic adaptive path, which re-checks operand shapes
at runtime. The common loop-counter and accumulator idioms stay fully
specialized. - Streaming
output_schemavalidation tolerates markdown code fences.
The incremental JSON validator behindschema_stream_abort(and the
std/json/stream*builtins) now strips a leading triple-backtick fence
(with an optional language tag such asjson) and a trailing closing fence
around the JSON body, surviving arbitrary chunk boundaries. Local Ollama
structured-output calls that wrap their JSON in a code fence no longer abort
withschema_stream_aborted. Genuine non-JSON leads and schema violations
inside the fence still fail as before. A root scalar (number /true/
false/null) is now framed at the first trailing whitespace or backtick,
so trailing junk after the value (e.g.42 garbage,4 2) is correctly
rejected as invalid rather than silently dropped. - Typed fast-path opcodes (
AddInt,LessInt,EqualString, …) now guard
their operands and fall back to generic semantics on a type miss instead of
hard-erroring. The compiler emits these from a static type guess, but a guess
can be wrong at runtime — anany-typed value flowing through a typed
parameter or an annotated binding initializer (let x: int = <any>) is not
runtime-checked, so the operand may be a different primitive than the
annotation claims. The optimized build previously threw a
specialization-internal error (e.g.Typed int add expected int operands, got int and float) on programs the unoptimized build runs correctly; it now
produces the same result as the unoptimized build by construction. The hot
path where the guess holds is unchanged, and genuinely incompatible operands
still error with the same generic message in both builds. - Sandboxed builds no longer poison the shared
sccachedaemon.sccache
runs as a single long-lived per-user server; if a sandboxed cargo build was the
first caller to spawn it, the daemon inherited harn'ssandbox-exec
confinement permanently (even after reparenting to launchd) and then failed
every later build machine-wide withOperation not permitted— unable to
read build inputs outside the sandbox root or write its cache dir under
~/Library/Caches. Sandboxed process spawns now bypass the rustc wrapper
(emptyCARGO_BUILD_RUSTC_WRAPPER/RUSTC_WRAPPER, which overrides
build.rustc-wrapperin.cargo/config.toml), so a per-command sandbox can
never confine the cross-workspace daemon. The on-disk cache and all
unsandboxed builds are unaffected.
Install / Upgrade
cargo install harn-cliFull Changelog: v0.8.81...v0.8.82