Section 03: ori_repr EscapeInfo Storage + Query Plumbing
Status: Not Started
Goal: Replace the 12-line EscapeInfo placeholder ZST in compiler/ori_repr/src/escape/mod.rs with the concrete FxHashMap<ArcVarId, Locality> storage shape locked in Section 01.3, and replace the hardcoded let result = true; body of ReprPlan::escapes() at plan/query.rs:111 with a real lookup mirroring the rc_strategy() pattern at plan/query.rs:124-134. This section is the plumbing layer that future repr-opt §08 will populate.
Success Criteria:
-
compiler/ori_repr/src/escape/mod.rsno longer containspub struct EscapeInfo;(the placeholder ZST). It defines a concrete struct with avar_escape: FxHashMap<ArcVarId, Locality>field -
EscapeInfohas 4 public methods:escape_scope,escapes,is_non_escaping,join_escape_scope -
join_escape_scopeis monotone — it never narrows. Verified by a test that callsjoin_escape_scope(var, HeapEscaping)thenjoin_escape_scope(var, BlockLocal)and asserts the final value isHeapEscaping, notBlockLocal -
compiler/ori_repr/src/plan/query.rs::ReprPlan::escapes()body no longer hardcodeslet result = true;. It consultsescape_info.get(&func).map(|info| info.escapes(var)).unwrap_or(true)(or equivalent) - Behavioral parity with the hardcode for the unanalyzed case:
escapes()returnstruefor any(func, var)pair where the function has not yet been analyzed byrepr-opt §08. Verified by a test that callsescapes()on an emptyReprPlanand asserts the result istrue - Behavioral departure from the hardcode for the analyzed case:
escapes()returns the actual computed value whenescape_infocontains a relevant entry. Verified by a test that constructs anEscapeInfowithvar → BlockLocaland assertsescapes()returnsfalse - EscapeInfo round-trips through
ori_arc::Localitycleanly (cross-crate behavioral test in 03.4) -
cargo test -p ori_reprgreen -
cargo test -p ori_arcgreen (no regression from the newEscapeInfoconsumingLocality) -
cargo check -p ori_reprandcargo check -p ori_llvmgreen (any downstream crate that re-exports or consumesEscapeInfostill compiles) - Connects upward to mission criteria: “EscapeInfo placeholder no longer exists”, “escapes() body replaced”
Context: Phase 2 research found that the existing EscapeInfo is a 12-line placeholder unit struct (pub struct EscapeInfo;) and the ReprPlan::escapes() query body hardcodes let result = true;. Both are deliberately stubbed for future repr-opt §08 to populate. This plan provides the storage shape and the query plumbing; §08 implements the analysis that fills the storage.
The work is small (~100 lines net) but it has two cross-crate boundaries that require care:
ori_repr→ori_arcimport: The newEscapeInfoimportsori_arc::aims::lattice::Locality. This is a new cross-crate import path, but the dependency direction (ori_repr → ori_arc) already exists percompiler/ori_repr/src/plan.rs:21(which importsori_arc::ArcVarId). No new dependency edge is added; only a new symbol is imported through the existing edge.- Behavioral parity for unanalyzed code: The current
escapes()hardcode returnstruefor everything. After the replacement, it must continue to returntruefor any(func, var)pair thatrepr-opt §08has not analyzed (which is currently all pairs, since §08 doesn’t exist yet). The default value ofEscapeInfo::escape_scope(returningLocality::Unknownfor unset variables) plusEscapeInfo::escapes’s> Locality::FunctionLocalpredicate gives the right answer (Unknown > FunctionLocalistrue), but the implementer must verify this behavioral parity with a test rather than reason from inspection alone.
Reference implementations:
compiler/ori_repr/src/plan/query.rs::ReprPlan::rc_strategyat lines 124-134: the canonical pattern this section mirrors. It reads from a per-key map (rc_strategies), falls back to a sensible default (Atomic { width: I64 }), and emits a tracing event. The newescapes()body should look structurally identical, just reading fromescape_infoinstead ofrc_strategiesand falling back totrue.compiler/ori_repr/src/plan.rs:21: existinguse ori_arc::ArcVarId;line. Proves cross-crate import fromori_reprtoori_arcis permitted by the existing dependency topology and not a new architectural decision.- Section 01.3 of this plan: the locked
EscapeInfoAPI design. Section 03 implements that exact code block.
Depends on: Section 02 (the Locality enum must have the ArgEscaping variant and the new comment in dimensions.rs before this section imports it; otherwise EscapeInfo’s storage type couldn’t represent the new variant).
03.1 Replace EscapeInfo placeholder with concrete storage
File(s): compiler/ori_repr/src/escape/mod.rs (currently 11 lines, ZST placeholder — re-verified during accuracy review)
Context: The current file is exactly (11 lines):
//! Escape analysis types.
//!
//! **Placeholder** — exports `EscapeInfo` so that
//! `ReprPlan::escape_info` compiles. Replaced when escape analysis
//! is implemented with the full connection graph and escape state framework.
/// Placeholder for per-function escape analysis information.
///
/// Replaced by the full `EscapeInfo` type when escape analysis is implemented.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EscapeInfo;
Section 03.1 replaces it with the locked design from Section 01.3. Note: compiler/ori_repr/src/plan.rs:71-74 currently has an #[expect(clippy::zero_sized_map_values, reason = "EscapeInfo is placeholder ZST — replaced when escape analysis is implemented")] attribute on the ReprPlan struct definition. This expectation must be removed as part of 03.1, because after the placeholder is replaced, EscapeInfo is no longer zero-sized and the expect lint will fire as “expectation unfulfilled.”
-
Re-read
compiler/ori_repr/src/escape/mod.rsto confirm it is still the 11-line placeholder. If it has been touched since plan creation, re-derive the replacement plan against the current content. -
Remove the
#[expect(clippy::zero_sized_map_values, ...)]attribute atcompiler/ori_repr/src/plan.rs:71-74(theReprPlanstruct attribute). After Section 03.1,EscapeInfois no longer ZST and the lint suppression is incorrect — leaving it would cause clippy to fire “this lint expectation is unfulfilled”. -
Replace the entire file with the concrete
EscapeInfoper Section 01.3://! Per-function escape analysis storage. //! //! Replaces the placeholder `EscapeInfo` ZST with concrete per-function //! escape facts. Populated by repr-opt §08's connection-graph escape //! analysis. Consumed by `ReprPlan::escapes()`, repr-opt §09's sharing //! bound analysis, and repr-opt §10's thread-locality analysis (which //! routes through `RcStrategy::NonAtomic`, NOT a parallel ThreadLocality //! enum — see plans/repr-opt/00-overview.md:192). //! //! ## Design //! //! The storage is a per-function `FxHashMap<ArcVarId, Locality>` where //! the value is the unified `ori_arc::aims::lattice::Locality` ordered //! lattice (BlockLocal < FunctionLocal < ArgEscaping < HeapEscaping < //! Unknown). Variables not present in the map default to `Unknown` //! (conservative — assumed to escape). //! //! The writer (`join_escape_scope`) is monotone: it widens but never //! narrows. This prevents accidental loss of precision in the ordered //! lattice from a producer that incorrectly tries to "downgrade" a //! variable's escape scope. use ori_arc::aims::lattice::Locality; use ori_arc::ArcVarId; // re-exported from ori_arc::ir; matches plan.rs:17 convention use rustc_hash::FxHashMap; /// Per-function escape analysis information. /// /// Maps each variable in a function to its computed escape scope. /// Populated by `repr-opt §08`'s connection-graph escape analysis. /// Consumed by `ReprPlan::escapes()`, `repr-opt §09`'s sharing bound /// analysis, and `repr-opt §10`'s thread-locality analysis. /// /// Variables not present default to `Locality::Unknown` (conservative). #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct EscapeInfo { var_escape: FxHashMap<ArcVarId, Locality>, } impl EscapeInfo { /// Query the escape scope of a variable. /// /// Returns `Locality::Unknown` if the variable is not present in the /// map (the conservative default — variables not yet analyzed are /// assumed to escape). #[must_use] pub fn escape_scope(&self, var: ArcVarId) -> Locality { self.var_escape.get(&var).copied().unwrap_or(Locality::Unknown) } /// Whether the variable escapes its function. /// /// True iff the variable's locality is `> FunctionLocal` — i.e., it /// has been observed flowing across a call boundary, to the heap, /// or to an unknown destination. Used by `ReprPlan::escapes()`. #[must_use] pub fn escapes(&self, var: ArcVarId) -> bool { self.escape_scope(var) > Locality::FunctionLocal } /// Whether the variable is provably non-escaping. /// /// True iff the variable's locality is `<= FunctionLocal` — i.e., it /// is known to stay within the defining function. Convenience for /// `repr-opt §09`'s `SharingBound::Unique` check. #[must_use] pub fn is_non_escaping(&self, var: ArcVarId) -> bool { self.escape_scope(var) <= Locality::FunctionLocal } /// Monotone widening: only widens, never narrows. /// /// The escape lattice is ordered (BlockLocal < FunctionLocal < /// ArgEscaping < HeapEscaping < Unknown), and the analysis discovers /// escape paths monotonically. This writer prevents a buggy producer /// from accidentally narrowing a variable's escape scope (which would /// be unsound — once a value is observed escaping, it cannot un-escape). /// /// If `var` is not yet in the map, it is inserted at the join of /// `BlockLocal` (the implicit starting point) and `scope`. If `var` /// is already in the map, the entry is replaced with the join of the /// old and new values. pub fn join_escape_scope(&mut self, var: ArcVarId, scope: Locality) { let entry = self.var_escape.entry(var).or_insert(Locality::BlockLocal); *entry = (*entry).join(scope); } } -
Verify the new file imports compile. Run
cargo check -p ori_reprand confirm zero errors. If theori_arc::aims::lattice::Localitypath has changed due to Section 00’s split, update the import accordingly. Per Section 00, the post-split path is stillori_arc::aims::lattice::Localitybecauselattice/mod.rsre-exportsstate::AimsStateand the dimension enums fromdimensions.rs. -
Verify the existing
ReprPlan::escape_info: FxHashMap<Name, EscapeInfo>field atcompiler/ori_repr/src/plan.rs:91still compiles. TheEscapeInfotype changed shape but not name, so the field declaration is unchanged. -
Run
cargo build -p ori_reprand verify zero errors and zero new warnings.
03.2 Replace ReprPlan::escapes() body to consult escape_info
File(s): compiler/ori_repr/src/plan/query.rs (the escapes() method body is currently at lines 105-114, with the actual hardcoded line at line 111)
Context: The current body is:
/// Check if a variable escapes its function scope.
///
/// Returns `true` by default — safe (never stack-promotes when unsure).
#[must_use]
pub fn escapes(&self, func: ori_ir::Name, var: ori_arc::ArcVarId) -> bool {
// Until escape analysis populates escape_info, assume everything escapes.
let result = true;
tracing::trace!(?func, ?var, escapes = result, "escapes query");
result
}
The replacement mirrors the rc_strategy() pattern at lines 124-134 of the same file:
/// Get the RC strategy for a type.
#[must_use]
pub fn rc_strategy(&self, idx: ori_types::Idx) -> RcStrategy {
let strategy = self
.rc_strategies
.get(&idx)
.copied()
.unwrap_or(RcStrategy::Atomic {
width: IntWidth::I64,
});
tracing::trace!(?idx, ?strategy, "rc_strategy query");
strategy
}
The new escapes() body follows the same shape: read from a per-key map, fall back to a sensible default, emit a tracing event.
-
Re-read
compiler/ori_repr/src/plan/query.rslines 100-140 to confirm both the oldescapes()body and therc_strategy()pattern are still in place. If either has changed, adapt the replacement. -
Replace the
escapes()body:/// Check if a variable escapes its function scope. /// /// Reads from `self.escape_info`. If the function has not yet been /// analyzed by `repr-opt §08`, returns `true` as the conservative default /// (matching the previous hardcoded behavior for unanalyzed code). Once /// `§08` populates `escape_info`, this query returns the actual computed /// answer. /// /// Mirrors the pattern of `rc_strategy()` at line 124 of this file: /// per-key map lookup with sensible default and tracing. #[must_use] pub fn escapes(&self, func: ori_ir::Name, var: ori_arc::ArcVarId) -> bool { let result = self .escape_info .get(&func) .map(|info| info.escapes(var)) .unwrap_or(true); tracing::trace!(?func, ?var, escapes = result, "escapes query"); result } -
Verify the replacement compiles. Run
cargo check -p ori_reprand confirm zero errors. -
Verify behavioral parity for the unanalyzed case. The previous hardcoded body always returned
true. The new body returnsself.escape_info.get(&func)... .unwrap_or(true). For anyfuncwhereescape_infodoes not contain an entry (i.e.,repr-opt §08has not analyzed it), the lookup returnsNone, the.unwrap_or(true)fires, and the return value istrue. Behavioral parity holds for the empty-analysis case. -
Verify behavioral departure for the analyzed case. The previous body could not return
false— every query returnedtrue. The new body returnsinfo.escapes(var)when the function has been analyzed, which can befalseif the variable is provably non-escaping. This is the desired behavioral departure — it’s the entire point of replacing the hardcode. -
Run
cargo test -p ori_reprand observe whether the existing test attests.rs:1574-1578(the only current caller ofescapes(), per Pass 1 Agent 1) still passes. The test currently asserts whatever it asserted with the hardcodedtrue— if it assertedescapes() == true, it should still pass for an emptyReprPlan(default escape_info is empty, fallback returnstrue).
03.3 Add EscapeInfo unit tests
File(s): compiler/ori_repr/src/escape/tests.rs (NEW — the placeholder file does not have a sibling tests file)
Context: The new EscapeInfo has 4 public methods (escape_scope, escapes, is_non_escaping, join_escape_scope) and a default value behavior. Each method needs a test, plus a test for the monotone-widening guarantee on join_escape_scope (which is the most subtle invariant).
Per compiler.md §Testing: “Tests in sibling tests.rs files (not inline).” Section 03.3 creates escape/tests.rs as a sibling and declares it from escape/mod.rs.
-
Add the
mod tests;declaration toescape/mod.rs(at the end of the file):#[cfg(test)] mod tests; -
Create
compiler/ori_repr/src/escape/tests.rswith tests for each method://! Tests for `EscapeInfo` storage and queries. //! //! Verifies the monotone-widening invariant on `join_escape_scope` and //! the behavioral parity of `escapes()` with the conservative default. use super::*; use ori_arc::ArcVarId; // re-exported from ori_arc::ir; matches plan.rs:17 convention fn var(id: u32) -> ArcVarId { ArcVarId::new(id) } // Default behavior #[test] fn default_escape_scope_is_unknown() { let info = EscapeInfo::default(); assert_eq!(info.escape_scope(var(0)), Locality::Unknown); } #[test] fn default_escapes_returns_true() { let info = EscapeInfo::default(); assert!(info.escapes(var(0)), "Default (no escape info) should conservatively assume escape"); } #[test] fn default_is_non_escaping_returns_false() { let info = EscapeInfo::default(); assert!(!info.is_non_escaping(var(0)), "Default (no escape info) should NOT be considered non-escaping"); } // Set-and-query behavior #[test] fn escape_scope_returns_set_value() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::FunctionLocal); assert_eq!(info.escape_scope(var(0)), Locality::FunctionLocal); } #[test] fn escapes_returns_false_for_function_local() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::FunctionLocal); assert!(!info.escapes(var(0)), "FunctionLocal does not escape (locality not > FunctionLocal)"); } #[test] fn escapes_returns_false_for_block_local() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::BlockLocal); assert!(!info.escapes(var(0))); } #[test] fn escapes_returns_true_for_arg_escaping() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::ArgEscaping); assert!(info.escapes(var(0)), "ArgEscaping is > FunctionLocal, so escapes() returns true"); } #[test] fn escapes_returns_true_for_heap_escaping() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::HeapEscaping); assert!(info.escapes(var(0))); } #[test] fn is_non_escaping_returns_true_for_block_local() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::BlockLocal); assert!(info.is_non_escaping(var(0))); } #[test] fn is_non_escaping_returns_true_for_function_local() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::FunctionLocal); assert!(info.is_non_escaping(var(0))); } #[test] fn is_non_escaping_returns_false_for_arg_escaping() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::ArgEscaping); assert!(!info.is_non_escaping(var(0)), "ArgEscaping is > FunctionLocal, so is_non_escaping() returns false"); } // Monotone widening invariant — the load-bearing test #[test] fn join_escape_scope_widens_but_never_narrows() { let mut info = EscapeInfo::default(); // First write: HeapEscaping (the wider value) info.join_escape_scope(var(0), Locality::HeapEscaping); assert_eq!(info.escape_scope(var(0)), Locality::HeapEscaping); // Second write: BlockLocal (a narrower value) info.join_escape_scope(var(0), Locality::BlockLocal); // The narrower write must NOT clobber the wider one — monotone invariant assert_eq!(info.escape_scope(var(0)), Locality::HeapEscaping, "join_escape_scope must NEVER narrow. A buggy producer trying to \ narrow HeapEscaping → BlockLocal is silently widened back to \ HeapEscaping (the join of the two)."); } #[test] fn join_escape_scope_widens_block_local_to_function_local() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::BlockLocal); info.join_escape_scope(var(0), Locality::FunctionLocal); assert_eq!(info.escape_scope(var(0)), Locality::FunctionLocal); } #[test] fn join_escape_scope_widens_to_unknown_for_unknown_writes() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::Unknown); assert_eq!(info.escape_scope(var(0)), Locality::Unknown); // Unknown is the top of the chain — nothing widens it further. info.join_escape_scope(var(0), Locality::HeapEscaping); assert_eq!(info.escape_scope(var(0)), Locality::Unknown); } // Multiple variables #[test] fn distinct_variables_are_independent() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::BlockLocal); info.join_escape_scope(var(1), Locality::HeapEscaping); assert_eq!(info.escape_scope(var(0)), Locality::BlockLocal); assert_eq!(info.escape_scope(var(1)), Locality::HeapEscaping); // var(2) is unset → Unknown assert_eq!(info.escape_scope(var(2)), Locality::Unknown); } -
Run
cargo test -p ori_repr -- escape::testsand verify all tests pass. The monotone-widening test (join_escape_scope_widens_but_never_narrows) is the load-bearing one — if it fails, the locked design from Section 01.3 has been mis-implemented.
03.4 Add cross-crate behavioral test (round-trip through ori_arc::Locality)
File(s): compiler/ori_repr/src/escape/tests.rs (extend the file from 03.3)
Context: The cross-crate boundary between ori_repr::EscapeInfo and ori_arc::Locality is the new architectural seam this section creates. A behavioral test that constructs an EscapeInfo, populates it with each Locality variant, and queries it back proves the seam works end-to-end and that no variant is lost in translation.
This is a small test but it’s explicitly named in the mission success criteria: “EscapeInfo round-trips through ori_arc::Locality cleanly.”
-
Add a round-trip test to
compiler/ori_repr/src/escape/tests.rs:/// Round-trip test: every Locality variant survives storage and retrieval /// through EscapeInfo. Validates the cross-crate boundary between /// ori_repr and ori_arc. #[test] fn round_trip_all_locality_variants() { let mut info = EscapeInfo::default(); info.join_escape_scope(var(0), Locality::BlockLocal); info.join_escape_scope(var(1), Locality::FunctionLocal); info.join_escape_scope(var(2), Locality::ArgEscaping); info.join_escape_scope(var(3), Locality::HeapEscaping); info.join_escape_scope(var(4), Locality::Unknown); assert_eq!(info.escape_scope(var(0)), Locality::BlockLocal); assert_eq!(info.escape_scope(var(1)), Locality::FunctionLocal); assert_eq!(info.escape_scope(var(2)), Locality::ArgEscaping); assert_eq!(info.escape_scope(var(3)), Locality::HeapEscaping); assert_eq!(info.escape_scope(var(4)), Locality::Unknown); // Each variant produces the right answer for the boolean helpers assert!(!info.escapes(var(0))); // BlockLocal: not escaping assert!(!info.escapes(var(1))); // FunctionLocal: not escaping assert!(info.escapes(var(2))); // ArgEscaping: escapes assert!(info.escapes(var(3))); // HeapEscaping: escapes assert!(info.escapes(var(4))); // Unknown: escapes assert!(info.is_non_escaping(var(0))); assert!(info.is_non_escaping(var(1))); assert!(!info.is_non_escaping(var(2))); assert!(!info.is_non_escaping(var(3))); assert!(!info.is_non_escaping(var(4))); } -
Add a behavioral parity test for
ReprPlan::escapes()in the appropriate file (compiler/ori_repr/src/tests.rs, where the existingescapes()test at line 1574-1578 lives):#[test] fn repr_plan_escapes_returns_true_for_unanalyzed_function() { // Behavioral parity with the previous hardcoded `let result = true;`. // For any (func, var) pair where escape_info does not contain an entry, // escapes() must continue to return true (the conservative default). let plan = ReprPlan::default(); let func = ori_ir::Name::new(0, 1) /* (shard, local) per compiler/ori_ir/src/name/mod.rs:33 */; let var = ori_arc::ArcVarId::new(0); assert!(plan.escapes(func, var), "Empty ReprPlan must return escapes()=true (conservative default)"); } #[test] fn repr_plan_escapes_returns_false_for_analyzed_non_escaping_var() { // Behavioral departure from the hardcode: when escape_info IS populated, // escapes() returns the actual computed answer, not the conservative default. let mut plan = ReprPlan::default(); let func = ori_ir::Name::new(0, 1) /* (shard, local) per compiler/ori_ir/src/name/mod.rs:33 */; let var = ori_arc::ArcVarId::new(0); let mut info = EscapeInfo::default(); info.join_escape_scope(var, ori_arc::aims::lattice::Locality::BlockLocal); plan.set_escape_info(func, info); assert!(!plan.escapes(func, var), "Populated ReprPlan must return escapes()=false for non-escaping vars"); } -
Verify the existing test at
compiler/ori_repr/src/tests.rs:1574-1578(which Pass 1 Agent 1 noted assertsassert_eq!(std::mem::size_of::<EscapeInfo>(), 0)) is deleted or updated. After Section 03.1 the size is no longer 0 (it’s the size of anFxHashMap), so the size assertion would fail. The right move is to delete the size assertion — it was a placeholder check, not a real invariant.- Find the line
assert_eq!(std::mem::size_of::<EscapeInfo>(), 0)(approximatelytests.rs:308per Pass 1 Agent 1) and delete it - Replace it with the new behavioral parity tests above (added to the same file or to
escape/tests.rs, whichever is more natural)
- Find the line
-
Run
cargo test -p ori_reprand verify all tests pass — the new EscapeInfo unit tests, the new round-trip test, the new behavioral parity tests, and all preexistingori_reprtests. -
Run
cargo test -p ori_arcand verify nothing regresses (the cross-crate import shouldn’t affectori_arc’s tests, but verify regardless).
03.R Third Party Review Findings
- None.
03.N Completion Checklist
-
compiler/ori_repr/src/escape/mod.rsno longer containspub struct EscapeInfo;ZST. It defines a struct withvar_escape: FxHashMap<ArcVarId, Locality>and 4 public methods -
EscapeInfoderivesDebug, Clone, Default, PartialEq, Eq(noHashbecauseFxHashMapis notHash) -
escape_scope,escapes,is_non_escaping,join_escape_scopeare all public, all#[must_use]where applicable -
join_escape_scopeis monotone — verified by thejoin_escape_scope_widens_but_never_narrowstest -
compiler/ori_repr/src/escape/tests.rsexists with at least 14 unit tests covering all 4 methods plus the round-trip -
compiler/ori_repr/src/plan/query.rs::ReprPlan::escapes()body no longer hardcodeslet result = true;. It consultsescape_info. - Behavioral parity test passes:
repr_plan_escapes_returns_true_for_unanalyzed_function - Behavioral departure test passes:
repr_plan_escapes_returns_false_for_analyzed_non_escaping_var - The deleted
assert_eq!(size_of::<EscapeInfo>(), 0)placeholder check is gone (or replaced with a comment explaining why it was removed) -
cargo test -p ori_reprgreen -
cargo test -p ori_arcgreen (no regression from cross-crate import) -
cargo check -p ori_llvmgreen (downstream crate that may consume ReprPlan still compiles) -
timeout 150 ./test-all.shgreen -
timeout 150 ./clippy-all.shgreen - Plan sync — update plan metadata to reflect this section’s completion:
- This section’s frontmatter
status→complete, subsection statuses updated -
00-overview.mdQuick Reference table status updated for Section 03 -
00-overview.mdmission success criteria checkboxes updated (EscapeInfo placeholder removed, escapes() body replaced) -
index.mdSection 03 status updated - Section 04’s
depends_onverified — Section 04 depends on["03"]
- This section’s frontmatter
-
/tpr-reviewpassed -
/impl-hygiene-reviewpassed -
/improve-toolingretrospective completed — for this section: was there a missing helper to “find all callers of a public method across crates” (the implementer needed to grep for.escapes(callers to verify behavioral parity)? Was there a missing diagnostic for “show the diff between cargo doc output before/after a public API change”? Implement every accepted improvement NOW (zero deferral) and commit each via SEPARATE/commit-push. -
/sync-claudesection-close doc sync — verify Claude artifacts across all section commits. Map changed crates to rules files, check CLAUDE.md, canon.md. Fix drift NOW.
Exit Criteria: wc -l compiler/ori_repr/src/escape/mod.rs shows the file is approximately 80-100 lines (was 12, has grown to hold the concrete struct + 4 methods + extensive doc comments). wc -l compiler/ori_repr/src/escape/tests.rs shows the new test file exists with 14+ test functions. cargo test -p ori_repr passes the full suite. The behavioral parity tests prove that any consumer of ReprPlan::escapes() continues to see true for unanalyzed functions and the actual computed answer for analyzed ones — no consumer change is needed in this section because the conservative default holds.