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
Layer 1 of the GC-correctness plan (docs/engine-plan.md, docs/src/internals/rfc-rooting-by-construction.md) is the only track that was never started. This issue is its campaign map: the ordered inventory of perry-codegen modules to migrate onto the crate::rooting combinators, one slice per PR.
Routing: linked from #7294. Design: #7459 (the RFC's own constructor was E0499, corrected). First lowering: #7461. Template slice: the PR that opens this issue.
What "migrated" means
Two properties, both checked by cargo test -p perry-codegen --lib rooting:::
The module names no expr::temp_root symbol. That module is the raw, order-sensitive API — push / get / set / truncate, plus guards the caller must remember to release — and every bug in the gc(layer 3): from-space quarantine catches 55 stale dereferences across the gap suite — the instrument is in CI but aimed at one synthetic fixture #7341 family was an ordering mistake against it. It is the escape hatch the RFC's step 3 says to #[deny] per module. Rust has no attribute that denies calling a pub(crate) function from one module, so the deny is a test over the module's own source, inlined by include_str!.
No raw heap pointer exists as a value the lowering can hold across a collection point.rooting::call_rooted returns a slot, never a register; rooting::call_with_roots re-reads each slot as part of emitting the consuming call; rooting::with_operands_rooted owns the release on every path out, including ?.
A slice finishes by adding its modules to MIGRATED_MODULES in crates/perry-codegen/src/rooting.rs. Removing a line is a regression, not a cleanup.
Acceptance per slice
Emitted IR byte-identical (normalised for register/label renumbering) on a program set that exercises the module's lowerings, or every difference itemised and justified — a difference is normally a real rooting bug the migration found, which is the point.
gc-root-dominance corpus green in both gated modes, including --seeded-violations 40.
cargo test -p perry-codegen --lib, -p perry-runtime --no-fail-fast.
The gap-test families that touch the module's lowerings, identical failure set on both arms.
Honest statement of what this buys
Measured on the template slice by sabotaging the migrated module four ways:
reintroduced shape
compiles?
caught by
#7192 in the borrow form (RootingEmitter, the RFC's design)
NO — E0499
rustc, via compile_fail doctest
hold the call_with_roots result across a later lowering
yes
nothing
the verbatim pre-#7453 code, via a bare ctx.block().call
yes
nothing — see below
reach back into expr::temp_root
yes
the ledger test
hold the operand guard so it can be released on one arm (#7462)
yes
the ledger test
So: on the real emitter the API does not make the bug fail to compile.FnCtx has no interior mutability, so the borrow-carrying Raw<'e> cannot be built on it (#7459/#7461 established this). What the migration buys is that the correct form is the only one the API can express, that the release cannot be branch-conditional, and that the escape hatch is denied per module. Getting an actual compile error needs the RefCell'd emitter of the RFC's step 1 — a separate, wider change.
Ordering
Hazard density first, because that is where the bug lives. haz counts sites where a raw-pointer binding is used after an intervening emission — the #7453/#7192 shape, measured on the emitter's own source (heuristic, over-reports; it is a worklist, not a verdict). raw counts .call(I64, "js_*") sites, which by perry's convention return a raw heap pointer (NaN-boxed values come back as DOUBLE). root counts existing rooting references, i.e. how much of the module is already hand-rooted.
expr/temp_root.rs becomes pub(in crate::rooting) — the raw API unreachable from any lowering, not merely uncounted. That is the same end state Layer 3 states for its raw accessor (#7457/#7458), and it is what makes the ledger permanent rather than a convention.
Layer 1 of the GC-correctness plan (
docs/engine-plan.md,docs/src/internals/rfc-rooting-by-construction.md) is the only track that was never started. This issue is its campaign map: the ordered inventory ofperry-codegenmodules to migrate onto thecrate::rootingcombinators, one slice per PR.Routing: linked from #7294. Design: #7459 (the RFC's own constructor was
E0499, corrected). First lowering: #7461. Template slice: the PR that opens this issue.What "migrated" means
Two properties, both checked by
cargo test -p perry-codegen --lib rooting:::expr::temp_rootsymbol. That module is the raw, order-sensitive API — push / get / set / truncate, plus guards the caller must remember to release — and every bug in the gc(layer 3): from-space quarantine catches 55 stale dereferences across the gap suite — the instrument is in CI but aimed at one synthetic fixture #7341 family was an ordering mistake against it. It is the escape hatch the RFC's step 3 says to#[deny]per module. Rust has no attribute that denies calling apub(crate)function from one module, so the deny is a test over the module's own source, inlined byinclude_str!.rooting::call_rootedreturns a slot, never a register;rooting::call_with_rootsre-reads each slot as part of emitting the consuming call;rooting::with_operands_rootedowns the release on every path out, including?.A slice finishes by adding its modules to
MIGRATED_MODULESincrates/perry-codegen/src/rooting.rs. Removing a line is a regression, not a cleanup.Acceptance per slice
gc-root-dominancecorpus green in both gated modes, including--seeded-violations 40.cargo test -p perry-codegen --lib,-p perry-runtime --no-fail-fast.Honest statement of what this buys
Measured on the template slice by sabotaging the migrated module four ways:
RootingEmitter, the RFC's design)compile_faildoctestcall_with_rootsresult across a later loweringctx.block().callexpr::temp_rootSo: on the real emitter the API does not make the bug fail to compile.
FnCtxhas no interior mutability, so the borrow-carryingRaw<'e>cannot be built on it (#7459/#7461 established this). What the migration buys is that the correct form is the only one the API can express, that the release cannot be branch-conditional, and that the escape hatch is denied per module. Getting an actual compile error needs theRefCell'd emitter of the RFC's step 1 — a separate, wider change.Ordering
Hazard density first, because that is where the bug lives.
hazcounts sites where a raw-pointer binding is used after an intervening emission — the #7453/#7192 shape, measured on the emitter's own source (heuristic, over-reports; it is a worklist, not a verdict).rawcounts.call(I64, "js_*")sites, which by perry's convention return a raw heap pointer (NaN-boxed values come back asDOUBLE).rootcounts existing rooting references, i.e. how much of the module is already hand-rooted.Whole campaign: 88 modules, 694 raw sites, 262 hazard sites, ~79k lines.
Slice 0 — DONE (the template)
History: #7453 #7461 #7462 #7463 #7464 #7465
1 modules · 669 lines · 11 raw sites · 7 hazard sites
expr/url_main.rsSlice 1a — array methods (largest hazard population)
1 modules · 1200 lines · 37 raw sites · 40 hazard sites
lower_array_method.rsSlice 1b — array search/copy methods
2 modules · 1564 lines · 40 raw sites · 26 hazard sites
expr/arrays_finds.rsexpr/array_methods.rsSlice 2 — instance methods + collections
3 modules · 3209 lines · 60 raw sites · 40 hazard sites
expr/instance_misc1.rsexpr/logical_collections.rslower_call/property_get/map_set.rsSlice 3 — literal accumulators (#7211 / #7280 home; NOT IR-identical)
History: #6951 #7211 #7280 #7602
4 modules · 1776 lines · 18 raw sites · 7 hazard sites
expr/objects_arrays_lit.rsexpr/array_literal.rsexpr/object_literal.rsexpr/array_push.rsSlice 4 — computed reads/writes (#7206 home)
History: #7206
9 modules · 6189 lines · 10 raw sites · 8 hazard sites
expr/index_get.rsexpr/index_get/guarded_array.rsexpr/index_get/inline_dyn_typed_array.rsexpr/index_set.rsexpr/index_set_typed_array.rsexpr/property_get.rsexpr/property_get/globalget.rsexpr/property_get/helpers.rsexpr/property_set.rsSlice 5 — call / construct paths (#7192 / #7207 home)
History: #7192 #7207
14 modules · 11138 lines · 70 raw sites · 9 hazard sites
lower_call/new.rslower_call/new_ctor_args.rsexpr/call_spread.rsexpr/this_super_call.rsexpr/super_method.rsexpr/new_dynamic.rslower_call/property_get/dynamic_dispatch.rslower_call/property_get/static_dispatch.rslower_call/property_get/number_string.rslower_call/property_get/promise_chain.rslower_call/func_ref.rslower_call/early_branches.rslower_call/scalar_method.rslower_call/mod.rsSlice 6 — strings / regex
2 modules · 2623 lines · 71 raw sites · 6 hazard sites
lower_string_method.rsexpr/string_regex_proc.rsSlice 7 — builtins, math, misc
16 modules · 16547 lines · 171 raw sites · 32 hazard sites
lower_call/builtin.rsexpr/misc_methods.rsexpr/math_simple.rsexpr/os_uri_dates.rsexpr/env_clones.rsexpr/bigint_set.rsexpr/compare.rsexpr/literals_vars.rsexpr/static_field_meta.rsexpr/static_method.rsexpr/helpers.rsexpr/binary.rsexpr/pod_record.rsexpr/index.rsexpr/i32_fast_path.rsexpr/mod.rsSlice 8 — leaves: crypto, native, ext, fetch
27 modules · 14252 lines · 174 raw sites · 82 hazard sites
lower_call/options/fetch.rslower_call/options/abort.rslower_call/options/mod.rslower_call/extern_func.rslower_call/extern_timers.rslower_call/namespace_call.rslower_call/native_module_dispatch.rslower_call/event_target.rslower_call/console_promise.rslower_call/ui_styling.rslower_call/native/mod.rslower_call/native/native_tui_layout_branch.rslower_call/native/native_instance_branch.rslower_call/native/native_ui_widgets_branch.rslower_call/native/native_ui_appshell_branch.rslower_call/native/jsonwebtoken.rsexpr/calls/crypto_kdf.rsexpr/calls/crypto_misc.rsexpr/calls/crypto_hash.rsexpr/calls/crypto_keys.rsexpr/calls/fs.rsexpr/child_proc.rsexpr/dyn_extern_i18n.rsexpr/js_runtime.rsexpr/native_memory.rsexpr/closure.rsexpr/fs_await.rsSlice 9 — codegen/ and stmt/ tail
12 modules · 18858 lines · 29 raw sites · 5 hazard sites
codegen/entry.rscodegen/closure.rscodegen/method.rscodegen/helpers.rscodegen/function.rscodegen/string_pool.rscodegen/arguments.rsstmt/loops.rsstmt/mod.rsstmt/masked_window_region.rsstmt/unused_expr.rsnative_value/materialize.rsUnassigned (add to a slice before starting it)
expr/temp_root.rsTerminal condition
expr/temp_root.rsbecomespub(in crate::rooting)— the raw API unreachable from any lowering, not merely uncounted. That is the same end state Layer 3 states for its raw accessor (#7457/#7458), and it is what makes the ledger permanent rather than a convention.Related findings from the template slice
URL.canParse(input, base)andURL.parse(input, base)still carried fix(codegen): root the URL constructor's coerced string across base lowering (Layer 1) #7453's window when the module was migrated — the same three lines fix(codegen): root the URL constructor's coerced string across base lowering (Layer 1) #7453 fixed innew URL(input, base), in the two static forms nobody re-read. Fixed in the template slice.gc_root_dominance_check.pycannot see the unrooted-register shape at all — filed separately.