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
{{ message }}
Repository navigation
fix(hy-emit): implement N5 zero-value map auto-allocation guards on index writes (fixes #645) - #663
var m map[k]v; m[k] = v works (first write allocates), where Go panics on a nil map. Why: removes an entire nil-shaped panic class from Hybrid-declared code at the cost of one emitted guard.
Previously, hy-emit did not emit N5 auto-allocation guards on map index writes (m[k] = v, b.counts[k] = v, compound assignments, or increment/decrement). As a result, writes to zero-valued maps or map fields in zero-valued structs panicked at runtime with panic: assignment to entry in nil map.
Changes
Emitter Type Tracking (compiler/crates/hy-emit/src/lib.rs):
Added var_map_types to Emitter and ScopeSnapshot to track declared map types for local variables and parameters.
Preserved full map type signatures in struct_fields (e.g. counts: map[string]int) rather than discarding them via base_ty_name.
Credit-close: #645 is fixed by landed #662 (Morphexe), merged to main as 74af832 — declaration-site zero-map allocation via the new zeroinit.rs. Thank you, @dezzy-music. This PR's approach was seriously considered as a composition (write-site guards on top of declaration-site allocation) and rejected for a concrete emission defect; details below.
Per-scenario verification on landed main (74af832)
This PR's test pins exactly the issue #645 repro program (var m map[string]int; m["a"] = 1 + var b bag; b.counts["k"] = 2). On the landed tree, without any write-site guard:
all allocate at declaration; corpus fixture n5-zero-map-first-write.hy pins all six byte-exact (differential 24+57, cargo 1281/0)
Why the guard take is not composed on top
Generic-struct fields emit invalid Go.map_type_of_place resolves a field's map type from the DECLARED field type text. For struct box[t] { vals map[string]t } and b := box[int]{}; b.vals["a"] = 1, the guard would emit if b.vals == nil { b.vals = make(map[string]t) } — t is not a type in scope; go build rejects it. Landed zeroinit.rs deliberately skips generic structs (documented) rather than emitting a wrong type.
Double evaluation. The guard re-emits the base place (emit_place(base) twice): for a call-result base (f()[k] = 1) the call runs twice — the emitter's never-a-wrong-emission contract prefers not to duplicate side effects. zeroinit.rs's header documents this as the reason for declaration-site lowering.
If the write-site-guard direction is wanted later (e.g. for make([]bag, n) element maps, which #662 explicitly does not cover), it needs place-typed resolution with alias resolution and type-argument substitution for generic structs — happy to review a re-worked minimal PR against current main.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves #645.
Summary
SPEC-LANGUAGE §3.1 N5 defines map zero-value semantics as:
Previously,
hy-emitdid not emit N5 auto-allocation guards on map index writes (m[k] = v,b.counts[k] = v, compound assignments, or increment/decrement). As a result, writes to zero-valued maps or map fields in zero-valued structs panicked at runtime withpanic: assignment to entry in nil map.Changes
compiler/crates/hy-emit/src/lib.rs):var_map_typestoEmitterandScopeSnapshotto track declared map types for local variables and parameters.struct_fields(e.g.counts: map[string]int) rather than discarding them viabase_ty_name.compiler/crates/hy-emit/src/stmt.rs):map_type_of_placeandstruct_type_of_placeto inspect index assignment targets (m[k],b.counts[k], nested struct fields).compiler/crates/hy-emit/src/tests.rs):test_n5_zero_map_auto_allocatetesting the exact repro program from [soundness] N5 not implemented: a zero-value map write (var m map[string]int; m["a"] = 1) passes hyb check, builds, and panics at runtime #645 (local map variable and struct map field writes).Verification
cargo test --manifest-path compiler/Cargo.toml -p hy-emit: 172/172 passed.python tools/check_conflict_markers.py: none.