Repository navigation
Held-out correctness fixes: nested shorthand, short-circuit, map_get nil, list values - #137
Conversation
…, map_get miss = nil, list value semantics
howlcipher
left a comment
There was a problem hiding this comment.
Dev-lead review (COMMENT-only), head d67884b5c69f0b9dd68670f1f13e831210bf8c5d, base c21a029c.
Verdict: not merge-ready yet, because of one small doc blocker (below). Code is sound. Once the doc is fixed, Okabe may undraft and squash-merge when CI is green.
Per fix
- Nested shorthand under governed: OK.
isSugarExprContextnow lists value-taking built-ins (write_file, exec, llm_generate, store_, req_, ...). This only controls where sugar expands. Authorization is unchanged: pre-desugar profile scan, then desugar, then post-desugarhfir.VerifyProfile, then build. So the expanded form is re-verified and nothing is allow-listed.TestHeldoutNestedShorthandGovernedFailsClosed(exec inside str_replace inside write_file) shows no artifact and no side file. The drift test walksconstruct.Table()and fails if a supported, non-opaque construct isn't a sugar parent, unless it's in the structural allow-list with a reason (defun/dict/neural_circuit/ephemeral_circuit). That pins new parent constructs. A new shorthand is still guarded by the strict default Scan, so unknown forms are denied. - Short-circuit and/or: OK. Interpreter
evalBinopdecides before evaluating the right side. AST bytecode and HFIR bytecode sharebytecode.ShortCircuit(a JUMP_IF_FALSE guard). Go/JS already used native&&/||. The CLI test haswrite_fileon the skipped side across -run, governed -run-bc and -compile-hfir-bc. The result is still bool (ToBool), not the operand value, so there is no truthiness semantics change. - map_get miss returns nil: code OK, docs incomplete. VM/bytecode/HFIR-bc push nil, JS returns null, Go dynamic maps return nil.
vm.TextOfkeeps print/stderr/to_string/str_join rendering nil as"", and Go/JS to_string/print match. task_api is migrated to(or (is_nil x) (= x "")), which is safe now that or short-circuits. release_authority's is_nil fallbacks are now live and tested. The ABI fixture changefalsetotrueis intentional and journaled. I found no unmigrated.howlcallers comparing a map_get result to""(the only hit is the new test). The numeric=uses in vm tests are on present keys.- Blocker (doc):
docs/reference/lowered_hfir_abi_v1.md:56still says a missing key is""andis_nilof it is false. That now contradicts the conformance fixture. Please update it. - Non-blocking stale notes:
internal/vm/map_get_path_test.go:13comment ("still """), andimprovements.md:937/981(#103 absence contract). Add a superseded note or update them. The historical journals can stay.
- Blocker (doc):
- Lists as values: OK. JS
howlFrameAppendcopies. Go usesappend(xs[:len(xs):len(xs)], item)in the direct form and the helper, so the backing array is never shared. The VM already copied. This is O(n) per append, so a loop of appends is O(n²) on all targets. That's acceptable for now, but worth a note for later perf work.
Go typed-map caveat
Go map[string]string can't hold nil, so a value read of a miss is still "". is_nil (map_get m k) is lowered to the key-presence check howlFrameMapGetIsNil, so is_nil agrees with the VM. But nz (map_get m k) d and (= (map_get m k) "") on a typed string map in Go still see "". That's a real cross-target divergence (the default doesn't fire in Go), not just a cosmetic limitation. It's documented in the journal, but not covered by a test that pins the divergence. Non-blocking: add a gogen test pinning current behavior, or lower nz(map_get …) via presence too. The list-in-map case isn't testable on Go because dict literals are string-valued, which predates this PR and is documented.
Benchmarks
The journal reports token-efficiency-v0 at 60/60 for each language and heldout-v0 at exit 0 with 126 verified and the same h31/h32 gaps. Committed results and frozen hashes are unchanged, and the experiments/token-efficiency* Go tests pass locally.
Scope, hard nos, CI
21 files: desugar plus drift test, vm, bytecode, hfir lowering, gogen, js, absence/path tests, task_api, release_authority test, ABI fixture, journal, CLI test. No prod -compile-bc/HFIR default flip, #90 untouched, no live LLM, no H1 claim. Local go test ./... passed on all packages. CI build succeeded on this head.
Fixes the four correctness bugs found by the held-out benchmark (#136). Base: main
c21a029c. No new built-ins, no token claims, no production-compile-bc/HFIR flip, #90 untouched.1. Nested shorthand under the governed profile
isSugarExprContextnow covers every value-taking built-in (write_file, to_lower, read_file, …).VerifyProfile, then build. A test checks that anexechidden in a nested shorthand produces no artifact and no effect.2. Short-circuit
and/orbytecode.ShortCircuit(a JUMP_IF_FALSE guard).write_fileon the skipped side does not run, andto_int "qq"there does not error.3.
map_getmiss → nil (behavior change)map[string]stringstill returns"", butis_nilof a namedmap_getis a key-presence check.""in print, stderr, to_string and str_join (vm.TextOf, plus Go/JS print/to_string).(= (map_get m k) "")on a miss is now false.apps/task_api(nowor is_nil … ""), theapps/release_authoritytest (deadis_nilfallbacks now fire), the vm/gogen/js absence/path tests, and the ABI conformance fixturemap_get_absence.4. Lists are values
appendrebinds only the named variable. To update a list held in a map,map_setit back.pushaliased: it now copies. Goappendcould share a backing array: it now uses a full slice expression.Tests
heldout_correctness_cli_test.go: each bug on the interpreter (-run), governed AST bytecode (-run-bc) and HFIR bytecode.Verification (no claims)
go build/vet/test ./...andgo test ./experiments/...pass.run.sh: 60/60 for each language.run.sh: 126/126 references verified, the same 2 gaps; results unchanged.Journal:
docs/journals/2026-10-12_heldout_correctness_fixes.md.Caveats: the JS
nzlowering bug predates this PR (aletin expression position); Go dict literals are string-valued only; Gonzon a typed string map can still see"".