Skip to content

docs: BUGS.md — audited compiler bug inventory#2

Merged
Haofei merged 1 commit into
mainfrom
docs/bugs-inventory
Jun 10, 2026
Merged

docs: BUGS.md — audited compiler bug inventory#2
Haofei merged 1 commit into
mainfrom
docs/bugs-inventory

Conversation

@Haofei

@Haofei Haofei commented Jun 10, 2026

Copy link
Copy Markdown
Owner

White-box audit of the rss compiler on 2026-06-10; every item reproduced against the release binary (commit f5fab92).

HIGH (5): prefix !/~ precedence inversion (silent wrong result); release-AOT integer overflow wraps while interp/debug trap; read-param reassignment lowers to non-compiling Rust (E0308); untyped Some() local bypasses arg type-check; turbofish struct ctor → E0423.
MEDIUM (5): <</>> share the comparison tier; multi-dot number literals accepted; mixed numeric operands skip check; reg_vm float == is bitwise (diverges from AOT IEEE); generic ctor drops its type arg.
LOW (1): ~O(n³) check blowup on nested generics.

Each entry has source location, root cause, a minimal repro, expected-vs-actual, and a suggested fix. See BUGS.md.

🤖 Generated with Claude Code

…low)

White-box audit on 2026-06-10 against the release binary; all items reproduced.
HIGH: prefix !/~ precedence inversion (silent wrong result); release-AOT integer
overflow wrap vs interp/debug trap; read-param reassign -> E0308; untyped Some()
bypasses arg type-check; turbofish struct ctor -> E0423.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Haofei Haofei merged commit ff9b7d1 into main Jun 10, 2026
0 of 2 checks passed
@Haofei Haofei deleted the docs/bugs-inventory branch June 10, 2026 21:22
Haofei added a commit that referenced this pull request Jun 13, 2026
…ument release fast path for package check

#1 (List<T>.clone() etc.): `.clone()` on a builtin value type whose clone the
checker resolves (List/Bytes/Buffer) typechecked via the Clone protocol but lowered
to a dangling `List_clone`-style call -> rustc E0425. The lowerer now emits Rust's
`.clone()` for these (they lower to Clone Rust types). Same frontend-accepts/
backend-fails class as the borrowed-match E0308 fix. Removes the need for manual
copy helpers in source-shaped ports. Regression test in checker_lowering/stdlib.

#2 (slow package check in debug): documented the release-binary fast path in
README (Performance) and cross-referenced from BUGS.md RSS-11 — debug leaves the
~O(n^3) generic-substitution path unoptimized; release is usable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei added a commit that referenced this pull request Jun 13, 2026
…generic params, File.open effects

- vm-jit: NativeModule::call now rejects an args slice whose length != the compiled
  function's n_params (the generated entry block reads exactly n_params words from
  args_ptr without bounding by n_args, so a short slice was an out-of-bounds read in
  a 'safe' API) — #1, the highest-severity finding. CompiledId now carries a
  per-module id so an id from another NativeModule is rejected instead of indexing
  the wrong table (#2). Tests: call_rejects_wrong_arg_count, call_rejects_id_from_another_module.
- lowerer: builtin_generic_type_params mapped Result to [K,V] (Map's params); split
  so Result uses [T,E] — fixes silently-failing generic substitution for
  Result.map/map_error/and_then (#7). Unit-test guard added.
- stdlib/fs/file.rssi: File.open/open_read/open_write now carry effects(native) so
  host file-resource creation is review-visible in the signature, consistent with
  the handle reads/writes (#6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei added a commit that referenced this pull request Jun 13, 2026
Controlled-assignment targets evaluate subexpressions (a field base, an index
expression) that are genuine reads of bindings. Several use/ident/effect/capture
passes only visited the assigned value, silently dropping those target reads.
Route them all through crate::hir::assign_target_reads / its AST analogue:

  local.rs:  collect_hir_stmt_idents, collect_hir_stmt_effect_events,
             collect_hir_stmt_inline_capture_uses,
             collect_closure_local_moved_uses_from_stmt,
             collect_retained_closure_captures_from_stmt
  body.rs:   collect_stmt_uses, collect_spawn_capture_idents_from_stmt,
             check_explicit_closure_captures_stmt
  analyzer:  check_match_exhaustiveness, check_unknown_bindings

This makes use-after-move, unknown-binding, capture, and effect analysis see
reads like obj/idx in 'obj.field[idx] = value' that were previously invisible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei added a commit that referenced this pull request Jun 13, 2026
Close the four remaining gaps in the dynamic-testing plan.

#3 Metamorphic differential tests (tests/metamorphic.rs): for known-valid
   programs spanning the executable subset, apply semantics-preserving rewrites
   (reformat, trivia injection, unused local, top-level reorder, dead function)
   and require every backend still agrees AND produces the same output as the
   base; structure-preserving transforms must also leave the review
   classification unchanged.

#7 Example-corpus differential execution (tests/examples_exec.rs): the static
   sweep already checks+lowers examples/scripts; this runs the pure synchronous
   subset across all backends and asserts agreement. The skip predicate is
   derived from the source (I/O, timing, async, plus one documented VM
   capability gap: struct-typed Map/Set keys in uop_interning).

#5 JIT arithmetic-edge matrix (tests/jit_arithmetic_edges.rs): the edges the
   generative suite skips - i64 add/mul overflow, div/mod by zero, MIN/-1
   div+mod (all trap cleanly), shift wrap, NaN, signed zero, float infinity
   (all agree). Each op lives in a pure helper called with read-args so the JIT
   actually compiles and guards it.

#2 cargo-fuzz harness (fuzz/): a detached-workspace crate (never built by
   cargo build --workspace) with two libFuzzer targets - parse_check (front-end
   never-panics) and format_idempotent (format(format(x)) == format(x)).

Adds common::differential::backends_agreed_stdout for the new sweeps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jun 15, 2026
Remove completed entries (now in git history) and keep only open/partial work:
- Dropped: symbol identity (#1), class/static members (#2, type-associated
  consts + static methods), clone consistency (#6), Option ergonomics (#7), plus
  the already-checked namespace isolation / pattern matching / tuples / lower_name.
- Narrowed: type aliases -> generic aliases only; default args -> construction
  helpers only (defaults done); closures -> named function values.
- A header now records what's done so far; 8 items remain.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jun 16, 2026
A left-linear chain of hundreds of `&&`/`||` lowered to a deeply-nested Rust
expression that overflows rustc's recursive parser/type-checker (the
RUST_MIN_STACK=2g workaround). `&&`/`||` are associative for both value AND
short-circuit/evaluation order, so the lowerer now emits a BALANCED tree
(O(log n) nesting). The chain is collected iteratively (its left spine is the deep
part) so this pass doesn't recurse n-deep. Generated crates also now
allow(unused_parens) for the (benign) regrouping parens.

Verified: a 400-deep `&&` chain compiles+runs on the DEFAULT stack (no
RUST_MIN_STACK) with VM==compiled results; balanced-shallow-output lowering test
(checker_lowering); full vm_eval_parity (87) + vm_eval (17) green; golden header
updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jun 16, 2026
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jun 20, 2026
Second review pass (medium/low):

- §3.2: clarify that new read helpers reuse the §7.2 fallback *proof* but NOT
  the ABI — current helpers (field_int/list_len/list_get_int) are Int-only and
  return a single i64, so non-Int and heap-returning reads (String, nested
  List/Struct, map values) are a real ABI design task (typed result / handle for
  heap results, extended handle domain) with its own §7.1 amendment + tests.
- Refresh stale regression offenders in plan §0.2/§3.0 and README findings #1/#2
  from baseline-20260620.json: list_sort 1.31 / map_int 1.19 / json 1.48 /
  dynamic_closure 1.66 / string_text 1.80 no longer match; current worst are
  tier-0 native_read_heap 2.59 / nested_struct_field 1.36 and native
  task_group_spawn 1.58 / bytes_scan 1.42. Added "re-read JSON before quoting".
- Correct the "native near-Rust" framing with nat/rust (not reg/rust): scalar
  ~1.4x but read_heap ~13.3x. README table now carries an explicit nat/rust
  column; plan numbers match the committed baseline.
- Mark plan status in progress (Phase 0 done; Set/task_group fixed) instead of
  "draft / not started".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jun 24, 2026
OSR now fires automatically when a loop is hot, not just under
RSS_JIT_OSR. Per-function OSR-candidate state is lazily computed + cached
(NotCandidate pays one hoisted check per call; only candidate loops count
backedges), and a backedge counter triggers try_osr at a threshold — after
which the loop runs native, so the counter cost is bounded to the warm-up
iterations. RSS_JIT_OSR stays as the eager/force path. Default differential
(now auto-OSR) byte-identical; non-OSR interpreter dispatch
(pure_loop_sum/bool_logic_loop) non-regression measured.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jun 24, 2026
…broadening)

A captured f64 is bit-reinterpreted from its i64 capture slot (not integer-
converted), reusing the native f64<->i64 bitcast convention, so a closure
capturing Floats inlines inside an OSR loop. Int/Bool captures unchanged;
non-scalar captures still bail. OSR differential byte-identical on/off
(float parity exact); 10.3x faster on osr_float_closure_loop. Positive +
negative tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jul 1, 2026
…uePush*Float)

Item #2 of the remaining-issues list: collection element-type breadth for Float
VALUES, riding the float-arg helper ABI. Three clean HostCall mirrors of
ListPushFloat:
- MapInsertFloat: insert a Float value into an Int-keyed Map<Int, Float>.
- DequePushBackFloat / DequePushFrontFloat: push a Float onto a Deque<Float>.

Each: vm-jit macro row + Fn type + struct field + heap_effect(MutatesInput) set +
test noop; reg_vm host impl (journaled write of VmValue::Float, bails on a wrong-
value-type collection) + wiring; translate value-type-flows-in-inference + helper
selection by float(value) across both whole-function and OSR lowering arms
(int_or_free keeps the Int/unconstrained path).

Deliberately deferred (documented as the demand tail): float-KEYED collections
(Set<Float>, SortedSet/SortedMap with Float keys) need float-key hashing/ordering
with NaN semantics; the Map/Deque READS (MatchMapGetFloat, Deque pop) need a new
value-typed JitInstr / Option result. The mechanical write pattern here applies
directly when those are added.

Verify (Docker dev): differential 33/0, runtime 397/0 (new
jit_acceptance_runs_float_map_and_deque_helpers runs natively), vm-jit 83/0,
reg_vm lib 31/0 default, default+native builds and clippy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jul 1, 2026
…#4 shipped)

Audit of the 9-item JIT follow-ups roadmap found the planning docs were stale:
items #2/#3/#4/#5 were already shipped this session. Verified each green and
corrected the docs to match reality.

- jit_acceptance: add two nested-loop OSR regression tests
  (native_osr_nested_inner_loop_matches_interpreter + ..._with_dirty_outer_...)
  — both byte-parity with the interpreter and osr_entries > 0. The OSR pipeline
  is already multi-loop-aware (detect_natural_loops/select_osr_candidate_loop);
  detect_single_natural_loop is diagnostics-only, not the OSR gate.
- vm-optimizing-jit-plan: J0.4 row → S0–S3 shipped (10 AllocatesResult helpers,
  mem_budget exact via Model-A refusal, §7.2 force-deopt-tested); S4 still future.
- jit-followups-roadmap (new): #2 (deque-pop fusion, OSR in-region path),
  #3 (flat-list read inlining), #4 (nested-loop OSR), #5 (allocate-only heap
  writes) marked done; remaining = #1 (on-demand), #3-remainder flat-struct,
  string split-fold, and the hard precise-deopt main line #7→#6→#8.

Runtime suite 403 passed (only red = known env-broken jit_perf_gate baseline);
vm-jit lib 83/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jul 1, 2026
…n at runtime (item #2)

The report's per-function "declined by cost model" verdict re-derived the
profitability from a bare re-translation, which loses profile-guided PICs (no
profile feedback at report time) — so the very case that most often stays
interpreted was not attributed.

Now `consult_profitability` takes the function name and records it in a new
`unprofitable_declined_fns` map (fn name → first decline reason) as the run
happens — ground truth. The report reads that map for the per-function verdict
instead of re-deriving, so a profile-guided PIC decline is now correctly
attributed to its function. `report` mode's per-region log line also gains the
`fn=...` tag, and the map is exposed in `to_json`.

Test: report_explains_cost_model_decline_for_polymorphic_pic now additionally
asserts the per-function `not native: declined by cost model` line (previously
only the summary block was reliable).

Verified: clippy 0; lib 276/0; runtime 453/0 (incl. all 11 report tests);
differential 33/0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Haofei pushed a commit that referenced this pull request Jul 1, 2026
…quisite)

Item #2 (shared transform driver) is explicitly gated on "add targeted deopt-stress
tests first" because the driver would touch ip-map composition. Adds that test: a
DEEP multi-transform OSR region — an inlined leaf call + a non-escaping Option<Int>
+ a non-escaping Result<Int,Int> (user variant), all dissolved by scalar replacement
in one hot loop, so several region transforms and their ip-maps compose. Run through
every fast backend INCLUDING deopt-every-safepoint (which bails at each native
safepoint and resumes via the composed ip-map). Byte-identical parity + osr_entries>0
is the guard: a wrong ip-map composition — the exact failure a shared transform
driver could introduce — would diverge here.

This makes the driver refactor itself verifiable when undertaken (the driver is a
larger, deopt-critical, zero-perf-gain change and is deliberately scoped as its own
effort now that its safety net exists).

Verified: the new test passes (deopt-every parity holds; OSR entered).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants