From f2c6f0bd6b4a308f9372753789d851323ac0fa16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Tue, 4 Aug 2026 08:08:41 +0200 Subject: [PATCH] chore(gc): delete the GcCallEffect::NeverReturns variant that is never constructed It has never been constructed since #7314 introduced it, so its three match arms in precise_roots.rs were dead and rustc warned about the variant on every build -- one of the warnings failing the Warnings gate. Deleting rather than wiring it up, because the classifier's own comment already records why it can never be enabled: the audit it rested on is false (js_throw_reference_error_tdz, js_throw_not_a_constructor and others are declared -> f64, not -> !), and since #7302 a throw UNWINDS rather than longjmps, so the call site is an invoke whose unwind edge needs relocations -- while those helpers allocate the Error they raise and can therefore collect. Suppressing the safepoint would leave the catch handler's roots stale after a move. That reasoning is preserved at the site, in the past tense. CLAUDE.md's kill-policy is the rule being applied: an unexercised mode is a decision nobody has made, and the losing mode should stop compiling. No behaviour change, and asserted rather than assumed: 01_nursery_churn reports 62 statepoints / 88 relocations before and after. --- .../perry-codegen/src/function/precise_roots.rs | 8 +------- crates/perry-codegen/src/gc_call_effects.rs | 17 ++++++----------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/crates/perry-codegen/src/function/precise_roots.rs b/crates/perry-codegen/src/function/precise_roots.rs index 401155b1cf..7aa9ac1492 100644 --- a/crates/perry-codegen/src/function/precise_roots.rs +++ b/crates/perry-codegen/src/function/precise_roots.rs @@ -315,8 +315,7 @@ fn lower_roots_for_rs4gc(lines: &[&str], root_ptrs: &[String]) -> Option if is_call && trimmed.ends_with(')') && !trimmed.contains(" asm ") { if let Some(callee) = direct_callee_name(line) { let leaf = match crate::gc_call_effects::classify_direct_callee(callee) { - crate::gc_call_effects::GcCallEffect::CannotCollect - | crate::gc_call_effects::GcCallEffect::NeverReturns => true, + crate::gc_call_effects::GcCallEffect::CannotCollect => true, crate::gc_call_effects::GcCallEffect::AllocNoReentry => { crate::codegen::helpers::gc_safepoint_only_contract_enabled() } @@ -712,7 +711,6 @@ pub(super) fn lower_precise_roots_to_native_stack( matches!( crate::gc_call_effects::classify_direct_callee(c), crate::gc_call_effects::GcCallEffect::CannotCollect - | crate::gc_call_effects::GcCallEffect::NeverReturns ) }); if let Some(report) = report.as_mut() { @@ -757,10 +755,6 @@ pub(super) fn lower_precise_roots_to_native_stack( let cannot_collect = direct_callee.is_some_and(|callee| { match crate::gc_call_effects::classify_direct_callee(callee) { crate::gc_call_effects::GcCallEffect::CannotCollect => true, - // Control never returns here: no relocation is consumed and - // the frame's roots are dead past the call. Deeper frames - // carry their own records. - crate::gc_call_effects::GcCallEffect::NeverReturns => true, // Under the explicit-safepoint contract the runtime // guarantees these helpers' triggers never consume this // frame's precise roots (they defer to a declared safepoint diff --git a/crates/perry-codegen/src/gc_call_effects.rs b/crates/perry-codegen/src/gc_call_effects.rs index 2c379dd99e..adb85f49de 100644 --- a/crates/perry-codegen/src/gc_call_effects.rs +++ b/crates/perry-codegen/src/gc_call_effects.rs @@ -23,14 +23,6 @@ pub(crate) enum GcCallEffect { /// consumed at this call site and it needs no statepoint. Without the /// contract these remain safepoints. AllocNoReentry, - /// The callee never returns to this call site (audited 2026-08-01: every - /// `js_throw*` helper funnels into `exception::js_throw`, which is - /// `-> !` — the `f64` results are unreachable ABI shape). No relocation - /// can ever be consumed downstream and the frame's roots are dead past - /// the call, so the site needs no metadata in ANY mode. Values the - /// helper itself holds are its own frame's responsibility - /// (`RuntimeHandleScope`/temp roots), exactly as for every helper call. - NeverReturns, Unknown, } @@ -113,9 +105,12 @@ pub(crate) fn classify_direct_callee(name: &str) -> GcCallEffect { | "js_validate_array_comparator" | "js_validate_array_map_callback" => GcCallEffect::AllocNoReentry, // NO `js_throw*` prefix arm. It used to classify the whole family - // `NeverReturns`, which suppresses the safepoint in every mode — the - // strongest classification in this table, and the only one applied by - // prefix rather than exact name. + // a `NeverReturns` classification that suppressed the safepoint in + // every mode — the strongest possible, and the only one that would be + // applied by prefix rather than exact name. That variant is DELETED, + // not merely unused: it was never constructed, so its three match arms + // in `precise_roots.rs` were dead, and the kill-policy in CLAUDE.md + // says an unexercised mode is a decision nobody has made. // // Two things make that unsafe. The audit it rested on is already // false: `js_throw_reference_error_tdz`, `js_throw_not_a_constructor`