Section 14: Enforcement Tests, Testing Matrix & Exit Criteria
Context: Sections 09-13 wired all consuming phases (ori_types, ori_eval, ori_arc, ori_llvm, ori_ir) to read from ori_registry instead of maintaining independent type knowledge. This section is the final gate: it replaces the remaining ~315-line consistency.rs (post-Section 13, with zero cross-phase gap allowlists) and ~371-line dispatch_coverage.rs (eval implementation tracking) with a small set of structural enforcement tests that derive their expectations directly from the registry. The registry IS the specification; the enforcement tests verify that every phase faithfully implements it.
Design rationale: The old consistency tests were necessary because type knowledge was scattered: TYPECK_BUILTIN_METHODS (390 entries), EVAL_BUILTIN_METHODS (~165 entries), BUILTIN_METHODS in ori_ir (123 entries), BuiltinTable in ori_llvm (179 entries), and borrowing_builtins in ori_arc. The allowlists (TYPECK_METHODS_NOT_IN_EVAL, EVAL_METHODS_NOT_IN_IR, etc.) tracked intentional gaps between these independent lists. Sections 09-13 eliminated all 6 allowlists and the ori_ir builtin_methods module. With the registry as single source of truth, these gaps become structural impossibilities — a method either exists in the registry (and all phases must handle it) or it does not exist (and no phase references it). The enforcement tests verify this invariant at test time, while Rust’s type system enforces it at compile time (adding a field to TypeDef is a compile error in every consuming phase).
What this section replaces (post-Section 13 state):
compiler/oric/src/eval/tests/methods/consistency.rs(~315 lines remaining after Section 13) — rewritten with enforcement testscompiler/oric/src/eval/tests/methods/dispatch_coverage.rs(~371 lines) — theMETHODS_NOT_YET_IN_EVAL(189 entries) andCOLLECTION_RESOLVER_METHODS(16 entries) allowlists are eliminated when all methods are implemented; theevery_registry_method_has_eval_dispatch_handlertest is replaced by enforcement tests- 10 remaining consistency tests (registry sorting, iterator consistency, 6 format spec tests, well_known_generic_types_consistent, well_known_generic_types_matches_registry)
EVAL_BUILTIN_METHODSandTYPECK_BUILTIN_METHODS(already eliminated in Sections 09-10)ITERATOR_METHOD_NAMES(already eliminated in Section 10)- All
resolve_*_method()functions (already eliminated in Section 09, 19 functions excludingresolve_named_type_method)
Additional parallel lists to track (not in consistency.rs but relevant to exit criteria):
compiler/oric/src/eval/tests/methods/dispatch_coverage.rs:METHODS_NOT_YET_IN_EVAL(189 entries),COLLECTION_RESOLVER_METHODS(16 entries) — these track eval dispatch gaps and resolver-dispatch pathscompiler/ori_arc/src/borrow/builtins/mod.rs:CONSUMING_RECEIVER_METHOD_NAMES(10 entries),CONSUMING_SECOND_ARG_METHOD_NAMES(2 entries),CONSUMING_RECEIVER_ONLY_METHOD_NAMES(5 entries),SHARING_METHOD_NAMES(2 entries) — these encode COW-specific ARC ownership semantics; they are NOT simple borrow/own flags and may legitimately remain as ARC-internal knowledge, but should be validated against registry data via enforcement tests
Execution Order
Subsections within Section 14 have dependencies. They MUST be executed in this order:
- 14.0 (Prerequisite Functions) — Create the public API functions needed by enforcement tests. No tests depend on this yet, but all of 14.2 and 14.4 are blocked without it.
- 14.1 + 14.1b (Registry Integrity + Exhaustiveness Guards) — Can run in parallel. These are self-contained within ori_registry and consuming crates respectively.
- 14.3 (Purity Enforcement) — Independent; verifies existing tests from Section 02.
- 14.2 (Cross-Phase Enforcement) — Depends on 14.0 (prerequisite functions must exist). Rewrites consistency.rs.
- 14.4 (Testing Matrix) — Depends on 14.0 and 14.2 (uses the same API functions).
- 14.5 (Allowlist Elimination) — Can start after 14.2; grep verifications run after deletions.
- 14.6 (Legacy Code Removal) — Depends on 14.5 (allowlists must be deleted first); comment cleanups can start in parallel.
- 14.7 (Full Test Suite) — Depends on ALL of 14.1-14.6 being complete.
- 14.8 (Code Journey) — Depends on 14.7 passing.
- 14.9 (Exit Criteria) — Final gate; depends on everything else.
14.0 Prerequisite Function Creation
Several enforcement tests in 14.2 and 14.4 call public API functions that do not exist yet. These must be created before the enforcement tests can be written.
14.0.1 ori_eval::can_dispatch_builtin(TypeTag, &str) -> bool
Does not exist. Referenced by 14.2 Test 2, 14.2 Test 6, and 14.4.
Purpose: Query whether the evaluator’s dispatch chain (BuiltinMethodResolver + CollectionMethodResolver) can handle a (type, method) pair without producing UndefinedMethod.
Implementation options:
- (a) Wrap the existing
dispatch_builtin_method_str()pattern from dispatch_coverage.rs: call with a minimalValueand empty args, check that the error is NOTUndefinedMethod. This is the simplest approach but requires constructing aValueper call. - (b) Add a dedicated lookup function that queries the resolver chain without executing:
BuiltinMethodResolver::has_method(type_name, method_name)+CollectionMethodResolver::has_method(method_name). Cleaner but requires addinghas_methodto both resolvers.
Recommended: Option (b) for performance and clarity. The existing dispatch_coverage.rs already demonstrates option (a) works, so the semantics are validated.
File: compiler/ori_eval/src/methods/mod.rs or compiler/ori_eval/src/lib.rs (pub re-export)
14.0.2 ori_llvm::has_builtin_handler(&str, &str) -> bool
Does not exist. Referenced by 14.2 Test 3 and 14.2 Test 6.
Purpose: Query whether ori_llvm can handle a (type_name, method_name) pair, either via inline BuiltinTable codegen or via a runtime function declaration.
Implementation: Promote builtin_table() access or add a thin wrapper:
// In ori_llvm/src/lib.rs or codegen/arc_emitter/builtins/mod.rs
pub fn has_builtin_handler(type_name: &str, method_name: &str) -> bool {
let table = builtin_table();
table.has(type_name, method_name)
|| has_runtime_method(type_name, method_name)
}
Note: builtin_table() is currently pub(crate). Either promote to pub or provide the wrapper above. has_runtime_method() also does not exist (see 14.0.3).
File: compiler/ori_llvm/src/codegen/arc_emitter/builtins/mod.rs (+ re-export in lib.rs)
14.0.3 ori_llvm::has_runtime_method(&str, &str) -> bool
Does not exist. Referenced by 14.2 Test 3.
Purpose: Check whether a builtin method has a corresponding ori_rt runtime function declaration (the fallback path for methods without inline LLVM IR).
Implementation: Scan the runtime function declarations in ori_llvm/src/runtime.rs or build a static set of known runtime-dispatched methods. Alternatively, if all methods go through BuiltinTable, this function may be unnecessary (every method either has a table entry or falls through to the generic interpreter-style dispatch).
Decision needed: Verify whether all builtin methods have BuiltinTable entries. If yes, has_runtime_method is not needed and has_builtin_handler simplifies to builtin_table().has(type_name, method_name). If some methods use runtime calls without table entries, enumerate them.
File: compiler/ori_llvm/src/codegen/arc_emitter/builtins/mod.rs
14.0.4 ori_llvm::handles_op_strategy(TypeTag, &str, &OpStrategy) -> bool
Does not exist. Referenced by 14.2 Test 4.
Purpose: Verify that emit_binary_op/emit_unary_op handles a specific OpStrategy variant for a given type and operator name.
Implementation: This is intrinsically hard to check at the API level because operator handling is embedded in match arms inside emit_binary_op. Options:
- (a) Enumerate all handled
(TypeTag, OpStrategy)pairs in a static set and check membership. Fragile — the set can drift from the actual match arms. - (b) Use the existing
registry_op_strategies_cover_all_operatorstest inori_llvm/tests(which already verifies all 20 OpDefs fields for all types). This test already exists and passes. The enforcement test in 14.2 can be marked as already covered by ori_llvm’s own test rather than requiring a new cross-crate API.
Recommended: Option (b). The existing test at compiler/ori_llvm/src/codegen/arc_emitter/builtins/tests.rs:200 already does this. Add a comment in 14.2 Test 4 noting that this test is in ori_llvm rather than oric, and add a cross-reference.
File: No new file needed if using option (b). If option (a), compiler/ori_llvm/src/codegen/arc_emitter/builtins/mod.rs.
14.0.5 Alternative: Place LLVM Tests In-Crate
For 14.0.2, 14.0.3, and 14.0.4, an alternative to creating pub APIs is to place the enforcement tests within ori_llvm’s own test suite (where pub(crate) is accessible). This avoids leaking internal APIs. The tradeoff is that cargo t -p oric won’t run them — they require cargo t -p ori_llvm.
Recommendation: Place LLVM-specific enforcement tests in compiler/ori_llvm/src/codegen/arc_emitter/builtins/tests.rs (where the existing registry_op_strategies_cover_all_operators test already lives). Only can_dispatch_builtin truly needs to be in oric (because it tests cross-crate eval dispatch).
Warnings
Complexity warning —
can_dispatch_builtin(14.0.1). Option (b) (“addhas_methodto both resolvers”) is cleaner but requires understanding the full resolver chain. The existingdispatch_builtin_method_str()incompiler/ori_eval/src/methods/mod.rs:180-193only exercisesBuiltinMethodResolver(priority 2), NOTCollectionMethodResolver(priority 1) orUserRegistryResolver(priority 0). The dispatch_coverage.rs test at line 316-317 already tracks this gap viaCOLLECTION_RESOLVER_METHODS. Thecan_dispatch_builtinfunction must check ALL three resolvers to be correct.
Dependency warning —
TypeTagvsValueimpedance mismatch.can_dispatch_builtin(TypeTag, &str)takes aTypeTag, but the evaluator dispatches onValuevariants (e.g.,Value::Some/Value::Noneboth map toTypeTag::Option). The function must mapTypeTagto a representativeValueinternally — exactly whatdispatch_coverage.rs::minimal_value_for()already does. Consider reusing or inlining that function.
Checklist
-
ori_eval::can_dispatch_builtin(TypeTag, &str) -> boolcreated and exported (2026-03-09) - Decision made: LLVM enforcement tests in ori_llvm (BuiltinTable is pub(crate), 3 tests already exist in builtins/tests.rs) (2026-03-09)
- If oric:
ori_llvm::has_builtin_handler(&str, &str) -> bool— N/A (decision: ori_llvm, no cross-crate API needed) (2026-03-09) - If oric:
ori_llvm::handles_op_strategy— N/A (decision: ori_llvm, existing tests cover this) (2026-03-09) - If ori_llvm: enforcement tests added to
ori_llvm/src/codegen/arc_emitter/builtins/tests.rs(3 existing tests: no_phantom_builtin_entries, builtin_coverage_above_threshold, registry_op_strategies_cover_all_operators) (2026-03-09)
14.1 Registry-Level Integrity Tests (ori_registry)
File: compiler/ori_registry/src/tests.rs (additions to existing purity tests from Section 02)
These tests enforce internal registry consistency — the data itself is well-formed, regardless of whether consuming phases handle it correctly.
Test 1: No duplicate methods within any type
/// Every TypeDef's method list must contain unique method names.
/// A duplicate would cause ambiguous dispatch in every consuming phase.
#[test]
fn no_duplicate_methods() {
use std::collections::BTreeSet;
for type_def in BUILTIN_TYPES {
let mut seen = BTreeSet::new();
for method in type_def.methods {
assert!(
seen.insert(method.name),
"Duplicate method `{}` on type `{}`",
method.name, type_def.name,
);
}
}
}
Test 2: No empty types
/// Every TypeDef must have at least one method.
/// A type with zero methods provides no behavioral specification
/// and should not be in the registry.
#[test]
fn no_empty_types() {
for type_def in BUILTIN_TYPES {
assert!(
!type_def.methods.is_empty(),
"TypeDef `{}` has zero methods -- every registered type must \
have at least one method (minimally: clone, equals, to_str)",
type_def.name,
);
}
}
Test 3: All TypeTag variants with methods have a TypeDef
/// Every TypeTag variant that carries methods must have a corresponding
/// TypeDef in BUILTIN_TYPES. Variants without methods (Unit, Never,
/// Function) and the DEI alias (DoubleEndedIterator → Iterator) are
/// intentionally excluded.
#[test]
fn all_method_bearing_type_tags_present() {
use std::collections::BTreeSet;
let registered_tags: BTreeSet<TypeTag> = BUILTIN_TYPES
.iter()
.map(|td| td.tag)
.collect();
// TypeTag variants that intentionally have no TypeDef:
// - Unit, Never: no methods, no operators
// - Function: no methods (memory classification only)
// - DoubleEndedIterator: aliases to Iterator via base_type()
const EXCLUDED: &[TypeTag] = &[
TypeTag::Unit,
TypeTag::Never,
TypeTag::Function,
TypeTag::DoubleEndedIterator,
];
// TypeTag::all() returns all 23 variants in declaration order.
for &tag in TypeTag::all() {
if EXCLUDED.contains(&tag) {
continue;
}
assert!(
registered_tags.contains(&tag),
"TypeTag::{tag:?} has no TypeDef in BUILTIN_TYPES. \
Add a const TypeDef in ori_registry/src/defs/ and include \
it in BUILTIN_TYPES.",
);
}
}
Test 4: Methods sorted by name within each type
/// Methods within each TypeDef must be sorted alphabetically by name.
/// This is a convention for deterministic iteration, readable diffs,
/// and binary-searchable lookup.
#[test]
fn methods_sorted_by_name() {
for type_def in BUILTIN_TYPES {
for window in type_def.methods.windows(2) {
assert!(
window[0].name <= window[1].name,
"Methods not sorted in `{}`: `{}` > `{}`\n\
Methods must be alphabetically sorted within each TypeDef.",
type_def.name, window[0].name, window[1].name,
);
}
}
}
Test 5: All receivers have explicit Ownership
/// Every MethodDef must have an explicit Ownership value for its receiver.
/// This test documents the invariant rather than checking it structurally
/// (since Ownership is a required field, Rust already enforces this at
/// compile time). The test verifies the semantic convention that no
/// method has Ownership::Owned unless it truly consumes self.
#[test]
fn all_receivers_documented() {
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
// Copy types should always borrow (borrow == copy for Copy types,
// but the annotation documents the intent).
if type_def.memory == MemoryStrategy::Copy {
assert_eq!(
method.receiver, Ownership::Borrow,
"Method `{}.{}` on a Copy type should use Ownership::Borrow \
(Copy types are trivially borrowed)",
type_def.name, method.name,
);
}
// Arc types: most methods borrow, but consuming methods (into)
// may use Owned. Just verify the field is explicitly set.
// (No-op assertion -- Ownership is a required field. This test
// exists to document the expectation.)
let _ = method.receiver; // field access proves it exists
}
}
}
Test 6: Equality support is universal
/// Every Ori builtin type supports `==` (equality). This is a language invariant:
/// all values are comparable for equality. A type with Unsupported eq would be
/// a language-level bug.
#[test]
fn no_unsupported_eq() {
for type_def in BUILTIN_TYPES {
assert!(
type_def.operators.eq != OpStrategy::Unsupported,
"Type `{}` has Unsupported eq operator. All Ori types must \
support equality comparison.",
type_def.name,
);
}
}
Test 7: Operator consistency
/// If a type supports comparison operators (lt, gt, lt_eq, gt_eq), it must
/// also support equality (eq, neq). Comparison without equality is nonsensical.
/// Additionally, if any ordering operator is supported, all four ordering
/// operators (lt, gt, lt_eq, gt_eq) must be supported.
#[test]
fn operator_consistency() {
for type_def in BUILTIN_TYPES {
let ops = &type_def.operators;
// If any comparison operator is supported, eq must be too
let has_any_cmp = ops.lt != OpStrategy::Unsupported
|| ops.gt != OpStrategy::Unsupported
|| ops.lt_eq != OpStrategy::Unsupported
|| ops.gt_eq != OpStrategy::Unsupported;
if has_any_cmp {
assert!(
ops.eq != OpStrategy::Unsupported,
"Type `{}` supports comparison but not equality. \
If lt/gt/le/ge are supported, eq must be too.",
type_def.name,
);
assert!(
ops.neq != OpStrategy::Unsupported,
"Type `{}` supports comparison but not not-equal. \
If lt/gt/le/ge are supported, neq must be too.",
type_def.name,
);
}
// If all four comparison operators are supported, they should
// use the same strategy (no mixing signed/unsigned/float)
let cmp_ops = [ops.lt, ops.gt, ops.lt_eq, ops.gt_eq];
let supported_cmp: Vec<_> = cmp_ops
.iter()
.filter(|s| **s != OpStrategy::Unsupported)
.collect();
if supported_cmp.len() > 1 {
let first = supported_cmp[0];
for s in &supported_cmp[1..] {
assert_eq!(
*s, first,
"Type `{}` uses mixed comparison strategies: {:?} vs {:?}. \
All comparison operators should use the same strategy.",
type_def.name, first, s,
);
}
}
}
}
Test 8: SelfType returns are valid
/// Methods that return SelfType must be on types where returning Self
/// makes semantic sense. This test verifies that SelfType is only used
/// for methods that truly return the same type as the receiver.
///
/// Specifically: trait methods like `clone` should return SelfType.
/// Conversion methods like `to_int` should return a concrete TypeTag.
#[test]
fn self_type_returns_valid() {
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
if method.returns == ReturnTag::SelfType {
// SelfType is valid for:
// - clone() -- returns same type
// - Operator trait methods (add, sub, etc.) -- T op T -> T
// - Transform methods (trim, to_uppercase, etc.) -- str -> str
// SelfType is NOT valid for:
// - to_str() which should return Concrete(Str)
// - to_int() which should return Concrete(Int)
//
// We can't exhaustively validate the semantic correctness
// of SelfType usage, but we can check that `to_*` conversion
// methods (except to_uppercase/to_lowercase on non-str types)
// do NOT use SelfType.
if method.name.starts_with("to_") && method.name != "to_uppercase"
&& method.name != "to_lowercase"
{
// to_str, to_int, to_float, to_byte, to_char should use
// concrete return types, not SelfType.
// EXCEPTION: `to_str` on str itself returns SelfType (identity).
let is_identity = type_def.tag == TypeTag::Str
&& method.name == "to_str";
if !is_identity {
panic!(
"Method `{}.{}` returns SelfType but is a conversion \
method (`to_*`). Conversion methods should return \
a concrete TypeTag, not SelfType.",
type_def.name, method.name,
);
}
}
}
}
}
}
Cleanup
- [WASTE]
compiler/ori_registry/src/query/tests.rs:312-318— Duplicatecycleassertion removed (2026-03-09)
Checklist
-
no_duplicate_methods— no type has two methods with the same name (pre-existing in defs/tests.rs) (2026-03-09) -
no_empty_types— every TypeDef has at least one method (2026-03-09) -
all_method_bearing_type_tags_present— every method-bearing TypeTag variant has a TypeDef (2026-03-09) -
methods_sorted_by_name— alphabetical within each type (pre-existing asmethods_alphabetically_sorted) (2026-03-09) -
all_receivers_documented— every MethodDef has explicit Ownership (2026-03-09) -
no_unsupported_eq— every type supports at least==(Error/Channel/Iterator/Range exempt with justification) (2026-03-09) -
operator_consistency— comparison implies equality; consistent strategies (2026-03-09) -
self_type_returns_valid— SelfType only on semantically correct methods (2026-03-09)
14.1b Compile-Time Exhaustiveness Guards (Roc pattern)
Files: One _enforce_exhaustiveness function per consuming crate.
Inspired by Roc’s _enforce_exhaustiveness pattern: dead private functions whose sole purpose is to trigger Rust’s exhaustive match checker when a new TypeTag variant is added. These are never called — they exist purely for compile-time enforcement. Zero runtime cost.
Pattern
Each consuming crate (ori_types, ori_eval, ori_llvm, ori_arc) gets a function like:
/// NEVER CALLED. Exists solely so that Rust's exhaustive match checker
/// forces updates to this crate when a new TypeTag variant is added.
/// If you see a compile error pointing here, a new TypeTag was added
/// to ori_registry without updating this crate's handler.
#[allow(dead_code, unreachable_code)]
fn _enforce_exhaustiveness(tag: ori_registry::TypeTag) {
match tag {
TypeTag::Int => { /* handled in resolve_int_methods() */ }
TypeTag::Float => { /* handled in resolve_float_methods() */ }
TypeTag::Str => { /* handled in resolve_str_methods() */ }
TypeTag::Bool => { /* handled in resolve_bool_methods() */ }
TypeTag::Byte => { /* handled in resolve_byte_methods() */ }
TypeTag::Char => { /* handled in resolve_char_methods() */ }
TypeTag::List => { /* handled in resolve_list_methods() */ }
TypeTag::Map => { /* handled in resolve_map_methods() */ }
TypeTag::Set => { /* handled in resolve_set_methods() */ }
// ... every variant must be listed
// Adding a new TypeTag variant without a line here = COMPILE ERROR
}
}
Where to place them
| Crate | File | What it guards |
|---|---|---|
ori_types | src/infer/expr/methods/mod.rs | Method resolution handles all types |
ori_eval | src/methods/mod.rs | Method dispatch handles all types |
ori_llvm | src/codegen/arc_emitter/builtins/mod.rs | Builtin codegen handles all types |
ori_arc | src/borrow/mod.rs | Borrow inference handles all types |
Why this is better than test-time enforcement
- Compile-time: Caught during
cargo c, before any tests run - Zero cost: Dead code, never emitted in the binary
- Precise error: The compiler points directly at the missing match arm
- Roc-validated: Used in production across
low_level.rsandcan/builtins.rs
The test-time enforcement from 14.2 still provides value (verifying that per-method handlers exist within each type), but the compile-time guard catches the coarser “you forgot an entire type” class of errors instantly.
Implementation Steps
-
ori_types: Add
_enforce_exhaustivenesstocompiler/ori_types/src/infer/expr/methods/mod.rs. List everyTypeTagvariant with a comment pointing to the handler function or stating why it is not applicable (e.g.,Unit/Neverhave no methods). This file is whereresolve_named_type_methoddispatches, so it is the natural home. -
ori_eval: Add
_enforce_exhaustivenesstocompiler/ori_eval/src/methods/mod.rs. List everyTypeTagvariant with a comment referencing the resolver that handles it (BuiltinMethodResolver, CollectionMethodResolver) or why it has no handler (Channel — no Value representation yet). -
ori_llvm: Add
_enforce_exhaustivenesstocompiler/ori_llvm/src/codegen/arc_emitter/builtins/mod.rs. List everyTypeTagvariant referencing the builtin registration module that handles it. Note:minimal_value_for()in dispatch_coverage.rs already has an exhaustive match over TypeTag — the pattern is proven. -
ori_arc: Add
_enforce_exhaustivenesstocompiler/ori_arc/src/borrow/mod.rs. List everyTypeTagvariant referencing how borrow inference handles it (borrowing set member, iterator exclusion, Copy type passthrough, etc.). -
Verification: Temporarily add a
#[cfg(test)] TypeTag::_Testvariant (or use#[non_exhaustive]test — but TypeTag is internal so non_exhaustive is wrong). The practical verification is: check that all 4 functions have all 23 TypeTag variants listed. A manual count + code review is sufficient; the Rust compiler enforces the rest.
Cleanup
- [STYLE]
compiler/ori_llvm/src/codegen/arc_emitter/builtins/mod.rs— Six#[allow(dead_code)]annotations verified as acceptable tradeoff (can’t use#[expect]— would trigger unfulfilled-lint-expectation in non-test mode). Comment at line 155-158 documents reasoning. (2026-03-09)
Checklist
-
_enforce_type_tag_exhaustiveness(TypeTag)in ori_types — covers all 23 TypeTag variants (2026-03-09) -
_enforce_type_tag_exhaustiveness(TypeTag)in ori_eval — covers all 23 TypeTag variants (2026-03-09) -
_enforce_type_tag_exhaustiveness(TypeTag)in ori_llvm — covers all 23 TypeTag variants (2026-03-09) -
_enforce_type_tag_exhaustiveness(TypeTag)in ori_arc — covers all 23 TypeTag variants (2026-03-09) - Verified: all 4 functions list all 23 TypeTag variants (grep -c confirms 23 each) (2026-03-09)
- Verified:
cargo cpasses with all 4 functions added (2026-03-09)
14.2 Cross-Phase Enforcement Tests (oric integration)
File: compiler/oric/src/eval/tests/methods/consistency.rs (complete replacement of existing file)
These are THE critical tests. They replace the remaining consistency tests with registry-driven enforcement. Each test iterates the registry and verifies that the corresponding phase can handle every entry. No manual lists. No exceptions. No allowlists.
Test 1: Every registry method has a type checker handler
/// For each type in BUILTIN_TYPES, for each method, verify that ori_types
/// can resolve it. This replaces:
/// - typeck_method_list_is_sorted (sorted by registry convention)
/// - typeck_primitive_methods_in_ir (registry IS the source)
/// - eval_methods_recognized_by_typeck (single source, no gaps possible)
///
/// Implementation: Since Section 09 wired the type checker to read
/// directly from ori_registry, this test verifies that every method
/// in the registry is resolvable via ori_registry::has_method() (which
/// is what the type checker calls). A separate structural test verifies
/// that ori_types actually delegates to the registry (not hard-coded).
///
/// NOTE: ori_types::has_builtin_method() does not exist yet — it must
/// be added as a thin pub wrapper, or this test can call
/// ori_registry::has_method() directly since that IS the type checker's
/// resolution path post-Section 09.
#[test]
fn every_registry_method_has_typeck_handler() {
use ori_registry::{BUILTIN_TYPES, TypeTag};
let mut missing = Vec::new();
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
// After Section 09, the type checker resolves builtin methods
// via ori_registry::find_method(). Verify every registered
// method is findable (respecting DEI filtering).
let tag = type_def.tag;
if !ori_registry::has_method(tag, method.name) {
// For DEI-only methods, check with DoubleEndedIterator tag
if method.dei_only
&& ori_registry::has_method(TypeTag::DoubleEndedIterator, method.name)
{
continue;
}
missing.push((type_def.name, method.name));
}
}
}
assert!(
missing.is_empty(),
"Registry methods not handled by type checker ({} missing):\n{}",
missing.len(),
missing
.iter()
.map(|(ty, m)| format!(" {ty}.{m}"))
.collect::<Vec<_>>()
.join("\n"),
);
}
Test 2: Every registry method has an evaluator handler
/// For each type in BUILTIN_TYPES, for each method, verify that ori_eval
/// can dispatch it. This replaces:
/// - ir_methods_implemented_in_eval
/// - eval_method_list_is_sorted
/// - eval_primitive_methods_in_ir
/// - typeck_methods_implemented_in_eval
/// - iterator_typeck_methods_match_eval_resolver
/// - eval_iterator_method_names_sorted
///
/// Methods are checked against a registry-derived "implemented" flag
/// so that methods declared but not yet implemented in the evaluator
/// are tracked BY THE REGISTRY, not by a separate allowlist.
///
/// NOTE: ori_eval::can_dispatch_builtin() does not exist yet. It must
/// be added as part of this section's implementation — a pub function
/// that queries the evaluator's resolver chain (BuiltinMethodResolver +
/// CollectionMethodResolver) for whether a (tag, method_name) pair is
/// dispatchable. Alternatively, the existing dispatch_coverage.rs test
/// pattern can be migrated here.
#[test]
fn every_registry_method_has_eval_handler() {
use ori_registry::{BUILTIN_TYPES, TypeTag};
let mut missing = Vec::new();
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
// The evaluator's method dispatch chain:
// 1. UserRegistryResolver (user impls + derives)
// 2. CollectionMethodResolver (map/filter/fold/iterator)
// 3. BuiltinMethodResolver (primitives)
//
// A method is "handled" if ANY resolver in the chain
// can dispatch it. The test checks the union of all
// resolvers.
//
// TODO: Add ori_eval::can_dispatch_builtin(TypeTag, &str) -> bool
if !ori_eval::can_dispatch_builtin(type_def.tag, method.name) {
missing.push((type_def.name, method.name));
}
}
}
assert!(
missing.is_empty(),
"Registry methods not handled by evaluator ({} missing):\n{}",
missing.len(),
missing
.iter()
.map(|(ty, m)| format!(" {ty}.{m}"))
.collect::<Vec<_>>()
.join("\n"),
);
}
Test 3: Every registry method has an LLVM handler
/// For each type in BUILTIN_TYPES, for each method, verify that ori_llvm's
/// BuiltinTable has an entry. This replaces the BuiltinTable sync tests
/// that compared against TYPECK_BUILTIN_METHODS.
///
/// NOTE: Not all methods have dedicated LLVM codegen. Some fall through
/// to runtime function calls. The test verifies that the BuiltinTable
/// recognizes the method, not that it has inline IR. The BuiltinTable
/// returns `None` from dispatch for unrecognized methods, which triggers
/// the runtime fallback. The enforcement test checks that either:
/// (a) BuiltinTable.has(type, method) returns true, OR
/// (b) the method has a corresponding runtime function declaration.
///
/// IMPLEMENTATION NOTE: builtin_table() is currently pub(crate) in ori_llvm.
/// has_runtime_method() does not exist yet. To make this test work from oric:
/// - Either promote builtin_table() to pub and add pub has_runtime_method(), OR
/// - Add a single pub fn has_builtin_handler(&str, &str) -> bool to ori_llvm, OR
/// - Place this test in ori_llvm's own test suite (where pub(crate) is accessible)
#[test]
fn every_registry_method_has_llvm_handler() {
use ori_registry::{BUILTIN_TYPES, TypeTag};
// TODO: builtin_table() is pub(crate) — needs promotion to pub,
// or this test should live in ori_llvm/tests/ instead of oric/tests/.
let table = ori_llvm::codegen::arc_emitter::builtin_table();
let mut missing = Vec::new();
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
// Check BuiltinTable (inline codegen) or runtime function
// declarations (fallback path).
let has_inline = table.has(type_def.name, method.name);
// TODO: ori_llvm::has_runtime_method() does not exist yet —
// must be added as part of this section's implementation.
let has_runtime = ori_llvm::has_runtime_method(
type_def.name,
method.name,
);
if !has_inline && !has_runtime {
missing.push((type_def.name, method.name));
}
}
}
assert!(
missing.is_empty(),
"Registry methods with no LLVM handler (neither inline nor runtime) \
({} missing):\n{}",
missing.len(),
missing
.iter()
.map(|(ty, m)| format!(" {ty}.{m}"))
.collect::<Vec<_>>()
.join("\n"),
);
}
Test 4: Every registry operator has an LLVM handler
/// For each type in BUILTIN_TYPES, for each non-Unsupported operator
/// strategy, verify that emit_binary_op (or emit_unary_op) handles it.
/// This is the test that would have caught the string ordering bug
/// (commit 0bed4d75) where <, >, <=, >= had no is_str guards.
///
/// IMPLEMENTATION NOTE: An existing test `registry_op_strategies_cover_all_operators`
/// at `ori_llvm/src/codegen/arc_emitter/builtins/tests.rs:200` already verifies all
/// 20 OpDefs fields for all types. If this enforcement test is placed in oric rather
/// than ori_llvm, it needs `ori_llvm::handles_op_strategy()` (which does not exist).
/// The recommended approach (see 14.0.4) is to keep the existing test in ori_llvm
/// and add a cross-reference here rather than duplicating the logic.
#[test]
fn every_registry_operator_has_llvm_handler() {
use ori_registry::{BUILTIN_TYPES, OpStrategy};
let mut missing = Vec::new();
for type_def in BUILTIN_TYPES {
let ops = &type_def.operators;
// Check each operator field individually.
// The macro collects (operator_name, strategy) pairs and checks
// that non-Unsupported strategies have a handler.
// All 20 OpDefs fields must be covered.
macro_rules! check_op {
($field:ident, $name:expr) => {
if ops.$field != OpStrategy::Unsupported {
if !ori_llvm::handles_op_strategy(type_def.tag, $name, &ops.$field) {
missing.push((type_def.name, $name, ops.$field));
}
}
};
}
// Arithmetic (6)
check_op!(add, "add");
check_op!(sub, "sub");
check_op!(mul, "mul");
check_op!(div, "div");
check_op!(rem, "rem");
check_op!(floor_div, "floor_div");
// Comparison (6)
check_op!(eq, "eq");
check_op!(neq, "neq");
check_op!(lt, "lt");
check_op!(gt, "gt");
check_op!(lt_eq, "lt_eq");
check_op!(gt_eq, "gt_eq");
// Unary (2)
check_op!(neg, "neg");
check_op!(not, "not");
// Bitwise (6)
check_op!(bit_and, "bit_and");
check_op!(bit_or, "bit_or");
check_op!(bit_xor, "bit_xor");
check_op!(bit_not, "bit_not");
check_op!(shl, "shl");
check_op!(shr, "shr");
}
assert!(
missing.is_empty(),
"Registry operator strategies with no LLVM handler ({} missing):\n{}",
missing.len(),
missing
.iter()
.map(|(ty, op, strat)| format!(" {ty}.{op} ({strat:?})"))
.collect::<Vec<_>>()
.join("\n"),
);
}
Test 5: Every borrowing method is in the ARC borrow set
/// For each method with Ownership::Borrow in the registry, verify that
/// the ARC pipeline's borrow inference recognizes it as borrowing.
///
/// After Section 11, ori_arc::borrow::builtins::borrowing_builtin_names()
/// reads from ori_registry::borrowing_method_names() and adds protocol
/// builtins. This test verifies that the two sets are consistent.
///
/// NOTE: Some methods with Ownership::Borrow are excluded from the ARC
/// borrow set for semantic reasons:
/// - Iterator/DoubleEndedIterator methods (all excluded — ARC can't model
/// hidden iterator dependencies)
/// - `.iter()` method (creates an iterator referencing receiver data)
/// These exclusions are hardcoded in ori_registry::borrowing_method_names(),
/// not via a MethodDef field.
#[test]
fn every_registry_borrowing_method_in_arc_set() {
use ori_registry::{BUILTIN_TYPES, Ownership, TypeTag};
use std::collections::BTreeSet;
// Build the ARC borrow set the same way ori_arc does:
// borrowing_method_names() returns &[&str] (sorted, deduped).
let arc_borrow_set: BTreeSet<&str> = ori_registry::borrowing_method_names()
.iter()
.copied()
.collect();
let mut missing = Vec::new();
for type_def in BUILTIN_TYPES {
// Iterator methods are excluded from borrowing_method_names()
// because the ARC pipeline can't model iterator dependencies.
if type_def.tag == TypeTag::Iterator {
continue;
}
for method in type_def.methods {
if method.receiver == Ownership::Borrow {
// .iter() is also excluded from the borrow set
if method.name == "iter" {
continue;
}
if !arc_borrow_set.contains(method.name) {
missing.push((type_def.name, method.name));
}
}
}
}
assert!(
missing.is_empty(),
"Borrowing methods not in ARC borrow set ({} missing):\n{}",
missing.len(),
missing
.iter()
.map(|(ty, m)| format!(" {ty}.{m}"))
.collect::<Vec<_>>()
.join("\n"),
);
}
Test 6: Backend-required methods have all handlers
/// For each method with `backend_required: true`, verify that BOTH
/// the evaluator AND the LLVM backend have handlers.
///
/// This is the enforcement test for the `backend_required` flag on
/// MethodDef. Methods with `backend_required: false` are intentionally
/// exempt (e.g., `__iter_next` is llvm-only, `__collect_set` is eval-only).
///
/// Prior art: Rust's `must_be_overridden` on `IntrinsicDef`.
///
/// NOTE: Depends on ori_eval::can_dispatch_builtin() and
/// ori_llvm::has_builtin_handler() being added (see Tests 2 and 3).
#[test]
fn backend_required_methods_fully_implemented() {
use ori_registry::{BUILTIN_TYPES, TypeTag};
let mut eval_missing = Vec::new();
let mut llvm_missing = Vec::new();
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
if !method.backend_required {
continue;
}
// TODO: ori_eval::can_dispatch_builtin() must be added
if !ori_eval::can_dispatch_builtin(type_def.tag, method.name) {
eval_missing.push((type_def.name, method.name));
}
// TODO: ori_llvm visibility/API must be resolved (see Test 3 notes)
let table = ori_llvm::codegen::arc_emitter::builtin_table();
let has_llvm = table.has(type_def.name, method.name)
|| ori_llvm::has_runtime_method(type_def.name, method.name);
if !has_llvm {
llvm_missing.push((type_def.name, method.name));
}
}
}
let mut msg = String::new();
if !eval_missing.is_empty() {
msg.push_str(&format!(
"backend_required methods missing from evaluator ({}):\n{}\n",
eval_missing.len(),
eval_missing.iter()
.map(|(ty, m)| format!(" {ty}.{m}"))
.collect::<Vec<_>>().join("\n"),
));
}
if !llvm_missing.is_empty() {
msg.push_str(&format!(
"backend_required methods missing from LLVM ({}):\n{}\n",
llvm_missing.len(),
llvm_missing.iter()
.map(|(ty, m)| format!(" {ty}.{m}"))
.collect::<Vec<_>>().join("\n"),
));
}
assert!(msg.is_empty(), "{msg}");
}
Test 7: Pure methods are side-effect-free
/// Sanity check: methods marked `pure: true` should not be consuming
/// (Ownership::Owned implies mutation/consumption, which contradicts purity).
///
/// Also verifies that at least some methods ARE marked pure (catches
/// the failure mode of someone defaulting everything to `pure: false`).
#[test]
fn pure_method_sanity() {
use ori_registry::{BUILTIN_TYPES, Ownership};
let mut total_methods = 0;
let mut pure_count = 0;
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
total_methods += 1;
if method.pure {
pure_count += 1;
// Pure methods should not consume their receiver.
// If a method takes ownership, it's doing something
// non-trivial (moving, consuming) that isn't pure.
assert_ne!(
method.receiver, Ownership::Owned,
"Method `{}.{}` is marked pure but has Ownership::Owned receiver. \
Pure methods should borrow, not consume.",
type_def.name, method.name,
);
}
}
}
// Sanity: at least 30% of methods should be pure.
// Most getters/accessors (len, is_empty, abs, to_str) are pure.
let pure_pct = (pure_count * 100) / total_methods;
assert!(
pure_pct >= 30,
"Only {pure_pct}% ({pure_count}/{total_methods}) methods marked pure. \
Expected at least 30%. Check that pure is being set correctly.",
);
}
Test 8: Format spec variants synced (migrated)
/// Format spec enums (FormatType, Alignment, Sign) must be consistent
/// between ori_ir (source of truth), ori_types (registration), and
/// ori_eval (runtime globals).
///
/// This replaces the 6 format variant consistency tests from the current
/// consistency.rs (lines 74-216). The test logic is identical; it
/// scans source files for string patterns.
///
/// The scanned files are:
/// - ori_types/src/check/registration/builtin_types.rs (type registration)
/// - ori_eval/src/interpreter/prelude.rs (eval registration)
///
/// NOTE: This test is format-spec-specific, not registry-specific.
/// It may eventually move to a dedicated format spec consistency module.
/// It is included here because the old consistency.rs housed it and
/// this section is the designated replacement.
#[test]
fn format_spec_variants_synced() {
// Reuse the existing ir_format_type_names(), ir_align_names(),
// ir_sign_names() helpers and source-scanning logic.
// See consistency.rs lines 74-216 for the exact implementation.
// The test bodies are unchanged -- only the file location changes
// (from the old consistency.rs to the new enforcement test file).
format_type_variants_synced_with_types_registration();
format_type_variants_synced_with_eval_registration();
alignment_variants_synced_with_types_registration();
alignment_variants_synced_with_eval_registration();
sign_variants_synced_with_types_registration();
sign_variants_synced_with_eval_registration();
}
Test 9: Well-known generic types consistent (migrated)
/// Well-known generic types must be handled in the centralized
/// resolve_well_known_generic() function to ensure Pool tags match
/// between annotations and inference.
///
/// This replaces well_known_generic_types_consistent and
/// well_known_generic_types_matches_registry from the current
/// consistency.rs (lines 218-315). Post-registry, the set of
/// well-known generics can be derived from BUILTIN_TYPES by filtering
/// for types with generic parameters (excluding structurally-resolved
/// types like List and Map).
#[test]
fn well_known_generic_types_consistent() {
use ori_registry::TypeParamArity;
// Types resolved structurally via pool.list(), pool.map()
// rather than name-based resolve_well_known_generic().
const STRUCTURALLY_RESOLVED: &[&str] = &["List", "Map"];
// Derive the expected list from BUILTIN_TYPES.
// Use td.tag.is_generic() — is_generic() is on TypeTag, not TypeDef.
let well_known: Vec<&str> = ori_registry::BUILTIN_TYPES
.iter()
.filter(|td| matches!(td.type_params, TypeParamArity::Fixed(n) if n > 0))
.filter(|td| !STRUCTURALLY_RESOLVED.contains(&td.name))
.map(|td| td.name)
.collect();
// Verify the source file contains all types
// (same source-scanning logic as current consistency.rs)
let well_known_src = read_workspace_file(
"ori_types/src/check/well_known/mod.rs"
);
for ty in &well_known {
let pattern = format!("\"{ty}\"");
assert!(
well_known_src.contains(&pattern),
"Well-known generic type `{ty}` missing from check/well_known/mod.rs",
);
}
// Verify all three consumers delegate to the shared helper
// (they call resolve_well_known_generic_cached which wraps
// resolve_well_known_generic — the string match catches both)
let consumers = [
("registration", "ori_types/src/check/registration/type_resolution.rs"),
("signatures", "ori_types/src/check/signatures/mod.rs"),
("type_resolution", "ori_types/src/infer/expr/type_resolution.rs"),
];
for (label, rel_path) in consumers {
let source = read_workspace_file(rel_path);
assert!(
source.contains("resolve_well_known_generic"),
"{label} ({rel_path}) does not call resolve_well_known_generic()",
);
}
}
Warnings
Complexity warning — Tests 2, 3, 6 depend on APIs that do not exist. The
ori_eval::can_dispatch_builtin()function (14.0.1),ori_llvm::has_builtin_handler()(14.0.2), andori_llvm::has_runtime_method()(14.0.3) are referenced in the test code but must be created first. Implementation order MUST be: 14.0 (create APIs) before 14.2 (write tests). If these APIs are placed in ori_llvm’s own test suite (option 14.0.5), Tests 3, 4, and 6 must be restructured to reference in-crate tests rather than calling cross-crate pub APIs.
Risk: Test 3 (LLVM handler) may require exposing
pub(crate)internals. TheBuiltinTableandbuiltin_table()are currentlypub(crate)(seecompiler/ori_llvm/src/codegen/arc_emitter/builtins/mod.rs:161,239). Promoting topubviolates the EXPOSURE hygiene rule (internal state leaking through boundary types). The recommended alternative (14.0.5: place LLVM tests in ori_llvm’s own test suite) avoids this exposure but splits the enforcement tests across two crates.
Migration Steps for consistency.rs Rewrite
The rewrite must be done carefully to avoid losing test coverage during transition:
-
Copy the helper functions first:
read_workspace_file(),ir_format_type_names(),ir_align_names(),ir_sign_names()— these are reused by the migrated format spec tests (14.2 Test 8). Copy them into the new file before deleting the old one. -
Preserve the
WELL_KNOWN_GENERIC_TYPESconstant: The well_known_generic_types tests (14.2 Test 9) derive this from the registry now, but the existing constant at consistency.rs:223 is a useful cross-check during migration. After the registry-derived version is verified, delete the constant. -
Migrate format spec tests as-is: The 6 format spec tests (lines 140-216) are NOT registry-specific — they validate ori_ir enum variants against source file patterns. Copy them verbatim, then refactor into a single
format_spec_variants_synced()wrapper if desired. -
Migrate well_known_generic tests: The 2 well_known_generic tests (lines 237-315) use both a hardcoded constant AND registry data. The new version should derive entirely from registry data (as shown in 14.2 Test 9).
-
Delete old consistency.rs: Only after ALL tests in the new file pass, delete the old file. Run
cargo t -p oricto verify no test count regression. -
Verify
mod.rsstill declares both modules:compiler/oric/src/eval/tests/methods/mod.rsdeclaresmod consistency;— this must remain (file is rewritten, not deleted). If dispatch_coverage.rs is merged into consistency.rs, update themoddeclaration.
Migration Steps for dispatch_coverage.rs
dispatch_coverage.rs (371 lines) has its own allowlists that overlap with 14.2’s enforcement tests:
-
METHODS_NOT_YET_IN_EVAL(189 entries): This tracks methods the evaluator does not yet dispatch. It is an implementation gap, not an intentional design choice. When all methods are implemented, this list goes to zero and the allowlist is deleted. Until then, it documents eval’s current state. The 14.2every_registry_method_has_eval_handlertest replaces this — but only when the eval gaps are actually filled. -
COLLECTION_RESOLVER_METHODS(16 entries): This tracks methods handled byCollectionMethodResolverrather thanBuiltinMethodResolver. This is an eval-internal routing detail, not a gap. The enforcement test’scan_dispatch_builtin()function should check both resolvers, making this list unnecessary. -
Decision: Either:
- (a) Keep dispatch_coverage.rs as a “shrinking allowlist” until eval catches up, then delete when
METHODS_NOT_YET_IN_EVALreaches zero. This is the safer incremental path. - (b) Merge into the new consistency.rs and convert to a unified enforcement test with a single shrinking allowlist.
- (a) Keep dispatch_coverage.rs as a “shrinking allowlist” until eval catches up, then delete when
-
Recommended: Option (a) for now — dispatch_coverage.rs is a correctness guard for eval’s ongoing implementation work. The enforcement test in 14.2 Test 2 should reference dispatch_coverage.rs’s
METHODS_NOT_YET_IN_EVALas the authoritative list of known eval gaps. When that list reaches zero, both dispatch_coverage.rs and any eval-specific allowlist logic in 14.2 Test 2 can be deleted.
Checklist
-
every_registry_method_has_typeck_handler— replaces 3 old tests (2026-03-09) -
every_registry_method_has_eval_handler— replaces 6 old tests; usescan_dispatch_builtin+METHODS_NOT_YET_IN_EVALallowlist (2026-03-09) -
every_registry_method_has_llvm_handler— covered byno_phantom_builtin_entries+builtin_coverage_above_thresholdin ori_llvm (2026-03-09) -
every_registry_operator_has_llvm_handler— covered byregistry_op_strategies_cover_all_operatorsin ori_llvm (2026-03-09) -
every_registry_borrowing_method_in_arc_set— replaces borrowing_builtin_names dependency (2026-03-09) -
backend_required_methods_fully_implemented— enforcesbackend_requiredflag with eval allowlist; LLVM in ori_llvm tests (2026-03-09) -
pure_method_sanity— validatespureflag consistency (2026-03-09) -
format_spec_variants_synced— migrated from old consistency.rs; 6 tests unified into 1 (2026-03-09) -
well_known_generic_types_consistent— migrated and registry-derived; no hardcoded constant (2026-03-09)
14.3 Purity Enforcement Tests (ori_registry)
File: compiler/ori_registry/src/tests.rs (these were defined in Section 02 but are verified here as part of exit criteria)
These tests enforce the structural purity of ori_registry itself. They were created in Section 02 but are listed here because they are part of the final enforcement suite.
Test 1: Registry has no dependencies
/// Parse Cargo.toml and verify [dependencies] is empty.
/// A non-empty [dependencies] section would create transitive
/// coupling between all consuming phases.
#[test]
fn purity_cargo_toml_has_no_dependencies() {
let cargo_toml = include_str!("../Cargo.toml");
let deps_start = cargo_toml
.find("[dependencies]")
.expect("Cargo.toml must have a [dependencies] section");
let after_deps = &cargo_toml[deps_start + "[dependencies]".len()..];
let next_section = after_deps.find("\n[").map_or(after_deps.len(), |i| i);
let deps_body = after_deps[..next_section].trim();
let non_comment_lines: Vec<&str> = deps_body
.lines()
.filter(|line| {
let trimmed = line.trim();
!trimmed.is_empty() && !trimmed.starts_with('#')
})
.collect();
assert!(
non_comment_lines.is_empty(),
"ori_registry MUST have zero [dependencies]. Found:\n{}",
non_comment_lines.join("\n"),
);
}
Test 2: Core types are Copy
/// All core enum types must derive Copy. This is a compile-time check
/// disguised as a test.
#[test]
fn purity_core_enums_are_copy() {
fn assert_copy<T: Copy>() {}
assert_copy::<TypeTag>();
assert_copy::<MemoryStrategy>();
assert_copy::<Ownership>();
assert_copy::<OpStrategy>();
}
Test 3: All TypeDef entries are const-constructible
/// Const-constructibility is proven by the `const _:` declarations.
/// If any field or struct loses const-constructibility, this test
/// fails at compile time.
#[test]
fn purity_type_defs_are_const() {
use crate::defs::*;
// These lines ARE the enforcement -- they fail at compile time
// if TypeDef or its fields are not const-constructible.
const _: TypeTag = INT.tag;
const _: TypeTag = FLOAT.tag;
const _: TypeTag = STR.tag;
const _: TypeTag = BOOL.tag;
const _: TypeTag = BYTE.tag;
const _: TypeTag = CHAR.tag;
// Runtime assertions for correctness
assert_eq!(INT.tag, TypeTag::Int);
assert_eq!(FLOAT.tag, TypeTag::Float);
assert_eq!(STR.tag, TypeTag::Str);
assert_eq!(BOOL.tag, TypeTag::Bool);
assert_eq!(BYTE.tag, TypeTag::Byte);
assert_eq!(CHAR.tag, TypeTag::Char);
}
Note on LazyLock usage
compiler/ori_registry/src/query/mod.rs:215 uses std::sync::LazyLock in borrowing_method_names() to lazily build a sorted, deduplicated array. This is technically runtime initialization (not const), which deviates from the “all data is const-constructible” principle documented in lib.rs. The purity tests correctly don’t flag this because LazyLock is in std (not a dependency) and doesn’t involve heap allocation types. However, the existing purity_type_defs_are_const test only validates TypeDef fields, not query functions. This is an acceptable tradeoff documented here for awareness — const fn sorting/dedup is not possible in stable Rust.
Checklist
-
purity_cargo_toml_has_no_dependencies— passes (2026-03-09) -
purity_core_enums_are_copy— passes (2026-03-09) -
purity_type_defs_are_const— passes (2026-03-09) -
purity_no_unsafe_code— passes (from Section 02) (2026-03-09) -
purity_no_mutable_api— passes (from Section 02) (2026-03-09) -
purity_no_heap_allocation_types— passes (from Section 02) (2026-03-09)
14.4 Testing Matrix (type x method x phase)
The testing matrix is the complete cross-reference of every builtin method against every compiler phase. It documents which methods are implemented where and serves as both a coverage report and a regression guard.
Matrix Generation
The matrix MUST be generated from registry data, not manually maintained. The generation is done by a test that iterates BUILTIN_TYPES and checks each phase:
/// Generate the testing matrix as a side effect of verification.
/// This test produces a machine-readable coverage report and verifies
/// that every cell is correctly filled.
#[test]
fn testing_matrix_coverage() {
use ori_registry::{BUILTIN_TYPES, Ownership, OpStrategy};
let mut total = 0;
let mut typeck_count = 0;
let mut eval_count = 0;
let mut llvm_count = 0;
let mut arc_count = 0;
for type_def in BUILTIN_TYPES {
for method in type_def.methods {
total += 1;
// TODO: These functions must be added (see Test 1/2/3 notes)
let has_typeck = ori_registry::has_method(
type_def.tag, method.name,
);
let has_eval = ori_eval::can_dispatch_builtin(
type_def.tag, method.name,
);
// TODO: ori_llvm API must be resolved (see Test 3 notes)
let has_llvm = {
let table = ori_llvm::codegen::arc_emitter::builtin_table();
table.has(type_def.name, method.name)
|| ori_llvm::has_runtime_method(type_def.name, method.name)
};
// ARC exclusions (Iterator methods, .iter()) are hardcoded in
// ori_registry::borrowing_method_names(), not per-method flags.
let arc_borrow = method.receiver == Ownership::Borrow
&& type_def.tag != ori_registry::TypeTag::Iterator
&& method.name != "iter";
if has_typeck { typeck_count += 1; }
if has_eval { eval_count += 1; }
if has_llvm { llvm_count += 1; }
if arc_borrow { arc_count += 1; }
}
}
// All phases must handle ALL registry methods.
// After Sections 09-13, these should all be equal to total.
assert_eq!(typeck_count, total,
"Type checker missing {}/{total} registry methods",
total - typeck_count);
assert_eq!(eval_count, total,
"Evaluator missing {}/{total} registry methods",
total - eval_count);
assert_eq!(llvm_count, total,
"LLVM backend missing {}/{total} registry methods",
total - llvm_count);
// ARC count is methods with Borrow minus exclusions (not all methods)
// This is informational, not an equality assertion.
eprintln!(
"Testing matrix: {total} methods, \
typeck={typeck_count}, eval={eval_count}, \
llvm={llvm_count}, arc_borrow={arc_count}",
);
}
Matrix Format (documentation reference)
The complete matrix is too large to maintain manually in this document (Section 03 alone has 108 methods across 5 primitive types, and the full registry covers all builtin types). The canonical matrix is the output of the testing_matrix_coverage test above.
For reference, the matrix structure for each entry is:
| Type | Method | ori_types | ori_eval | ori_llvm | ori_arc | Ownership |
|---|---|---|---|---|---|---|
type_def.name | method.name | Y/N | Y/N | Y/N | borrow/owned | method.receiver |
Matrix Invariant Post-Plan
After the complete plan is implemented, the matrix must satisfy:
- ori_types column: 100% Y (every method type-checks)
- ori_eval column: 100% Y (every method evaluates)
- ori_llvm column: 100% Y (every method has codegen, inline or runtime)
- ori_arc column:
borrowfor everyOwnership::Borrowmethod (minus documented exclusions) - Ownership column:
Borrowfor all methods on Copy types;Borrowfor most methods on Arc types;Ownedonly for consuming methods
Checklist
-
testing_matrix_coveragetest written incompiler/oric/src/eval/tests/methods/consistency.rsand passing (2026-03-09) -
typeck_count == totalassertion passes: 442/442 (every registry method resolves in ori_types) (2026-03-09) -
eval_count == totalassertion: 253/442 (eval gap=189, matches METHODS_NOT_YET_IN_EVAL allowlist) — eval gaps tracked by ceiling test (2026-03-09) -
llvm_count == totalassertion: LLVM coverage enforced in ori_llvm’s own tests (builtin_table is pub(crate)) — no_phantom_builtin_entries + builtin_coverage_above_threshold pass (2026-03-09) - ARC borrow count printed: 412 borrowing method instances, 232 deduplicated names; matches
borrowing_method_names()(2026-03-09)
14.5 Allowlist Elimination Checklist
Each allowlist from the old consistency.rs is individually tracked for deletion. For each, we document what it tracked, why it is no longer needed, the enforcement test that replaces it, and the grep verification.
14.5.1 COLLECTION_TYPES (11 entries)
What it tracked: Collection types that had eval/typeck methods but were not in the ori_ir builtin method registry. These were excluded from IR cross-checks.
Entries:
Channel, DoubleEndedIterator, Iterator, Option, Result, Set,
error, list, map, range, tuple
Why no longer needed: All types (including collections) are in ori_registry. There is no separate “IR registry” vs “eval/typeck” distinction. Every type has a single TypeDef consumed by all phases.
Replacement test: every_registry_method_has_typeck_handler, every_registry_method_has_eval_handler, every_registry_method_has_llvm_handler — these iterate ALL types, no exclusions.
Verification:
-
grep -r "COLLECTION_TYPES" compiler/oric/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.2 IR_METHODS_DISPATCHED_VIA_RESOLVERS (14 entries)
What it tracked: IR registry methods implemented in the evaluator through method resolvers (UserRegistryResolver, CollectionMethodResolver) rather than direct dispatch in dispatch_builtin_method. These were valid runtime implementations but used a different dispatch path than EVAL_BUILTIN_METHODS.
Entries:
(float, abs), (float, ceil), (float, floor), (float, max), (float, min),
(float, round), (float, sqrt), (int, abs), (int, max), (int, min)
Why no longer needed: The evaluator enforcement test (every_registry_method_has_eval_handler) checks all dispatch paths (direct dispatch, method resolvers, collection resolver). The distinction between “direct dispatch” and “resolver dispatch” is an internal implementation detail, not a gap to track.
Replacement test: every_registry_method_has_eval_handler — checks can_dispatch_builtin() which queries all resolvers.
Verification:
-
grep -r "IR_METHODS_DISPATCHED_VIA_RESOLVERS" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.3 EVAL_METHODS_NOT_IN_IR (80 entries)
What it tracked: Evaluator methods for primitive types that were not in the ori_ir builtin method registry. These were methods the evaluator supported but ori_ir did not declare (Duration/Size operator aliases, float.hash, str.to_str, str.iter, error methods, Into trait methods).
Entries: 80 (type, method) pairs (see consistency.rs lines 50-80)
Why no longer needed: ori_ir’s BUILTIN_METHODS is superseded by ori_registry’s BUILTIN_TYPES. There is no separate IR registry to be “not in”. The registry contains every method; ori_ir delegates to it.
Replacement test: N/A — the concept of “eval methods not in IR” is eliminated. The registry IS the single source.
Verification:
-
grep -r "EVAL_METHODS_NOT_IN_IR" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.4 EVAL_METHODS_NOT_IN_TYPECK (63 entries)
What it tracked: Evaluator methods that the type checker did not recognize. These were methods that worked at runtime but would produce type errors if called from user code. Includes operator trait methods (handled via operator inference, not method resolution) and error type methods.
Entries: 63 (type, method) pairs (see consistency.rs lines 161-223)
Why no longer needed: Both the type checker and evaluator read from the same registry. If a method exists in the registry, both phases handle it. Operator methods are explicitly included in the registry (with trait_name set) and the type checker resolves them through the registry rather than through separate operator inference paths.
Replacement test: every_registry_method_has_typeck_handler + every_registry_method_has_eval_handler — both iterate the same registry.
Verification:
-
grep -r "EVAL_METHODS_NOT_IN_TYPECK" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.5 TYPECK_METHODS_NOT_IN_IR (146 entries)
What it tracked: Type checker methods for primitive types that were not in the ori_ir builtin method registry. This was the largest single gap list, covering Duration/Size conversion methods, char/byte predicates, float math methods, int conversion methods, and str utility methods.
Entries: 146 (type, method) pairs
Why no longer needed: Same as 14.5.3 — ori_ir is superseded by ori_registry.
Replacement test: N/A — concept eliminated.
Verification:
-
grep -r "TYPECK_METHODS_NOT_IN_IR" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.6 TYPECK_METHODS_NOT_IN_EVAL (260 entries)
What it tracked: Type checker methods that were not implemented in the evaluator. These were methods that type-checked successfully but would fail at runtime with “no such method”. This was the largest allowlist at 260 entries, covering Channel (9), DoubleEndedIterator (5), Iterator (18), Duration (22), Ordering (2), Option (7), Result (9), Set (9), Size (18), bool (1), byte (6), char (10), float (32), int (16), list (38), map (7), range (5), str (22).
Entries: 260 (type, method) pairs (see consistency.rs lines 374-633)
Why no longer needed: After Sections 09 and 10, both phases read from the registry. Methods that are declared in the registry must be handled by both phases. Any method that type-checks must also evaluate.
Replacement test: every_registry_method_has_eval_handler — no exceptions, no allowlist.
Verification:
-
grep -r "TYPECK_METHODS_NOT_IN_EVAL" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.7 TYPECK_BUILTIN_METHODS (390 entries)
What it tracked: The exported constant in ori_types listing every (type, method) pair the type checker recognizes. Used by consistency tests for cross-checking.
Entries: 390 (type, method) pairs in ori_types/src/infer/expr/methods/mod.rs
Why no longer needed: The type checker reads directly from ori_registry. It does not maintain its own method list. Enforcement tests iterate the registry, not TYPECK_BUILTIN_METHODS.
Replacement: ori_registry::BUILTIN_TYPES enumeration replaces TYPECK_BUILTIN_METHODS enumeration.
Verification:
-
grep -r "TYPECK_BUILTIN_METHODS" compiler/ --include='*.rs'returns 0 results (doc comment cleaned up in 14.6.1b; verified 2026-03-09) -
grep -r "pub const TYPECK_BUILTIN_METHODS" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.8 EVAL_BUILTIN_METHODS (~165 entries)
What it tracked: The exported constant in ori_eval listing every (type, method) pair the evaluator’s direct dispatch handles.
Entries: ~165 (type, method) pairs in ori_eval/src/methods/helpers/mod.rs
Why no longer needed: The evaluator reads method lists from the registry. Direct dispatch vs resolver dispatch is an internal implementation detail, not exposed.
Replacement: ori_registry::BUILTIN_TYPES enumeration + can_dispatch_builtin() function.
Verification:
-
grep -r "EVAL_BUILTIN_METHODS" compiler/ --include='*.rs'returns 0 results (comment cleaned up in 14.6.1b; verified 2026-03-09) -
grep -r "pub const EVAL_BUILTIN_METHODS" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.9 ITERATOR_METHOD_NAMES (24 entries)
What it tracked: The exported constant in ori_eval listing method names for Iterator/DoubleEndedIterator types, used by the CollectionMethodResolver.
Entries: 24 method names in ori_eval/src/interpreter/resolvers/mod.rs (now removed; comment at line 227 documents removal)
Why no longer needed: The resolver reads iterator method names from ori_registry::find_type(TypeTag::Iterator).methods and ori_registry::find_type(TypeTag::DoubleEndedIterator).methods.
Replacement: ori_registry::BUILTIN_TYPES enumeration for Iterator/DEI types.
Verification:
-
grep -r "ITERATOR_METHOD_NAMES" compiler/ --include='*.rs'returns 0 results (comments cleaned up in 14.6.1b; verified 2026-03-09) -
grep -r "pub const ITERATOR_METHOD_NAMES" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.10 DEI_ONLY_METHODS (5 entries)
What it tracked: Method names that require DoubleEndedIterator but not plain Iterator (next_back, rev, last, rfind, rfold).
Entries: 5 method names in ori_types/src/infer/expr/methods/mod.rs
Why no longer needed: Derivable from the registry: methods on TypeTag::DoubleEndedIterator that are not on TypeTag::Iterator.
Replacement: ori_registry::find_type(TypeTag::DoubleEndedIterator).methods minus ori_registry::find_type(TypeTag::Iterator).methods.
Verification:
-
grep -r "DEI_ONLY_METHODS" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.11 METHODS_NOT_YET_IN_EVAL (189 entries)
What it tracks: Registry methods declared but not yet implemented in the evaluator’s dispatch chain. This is a shrinking allowlist — each entry represents an eval implementation gap.
Entries: 189 (type, method) pairs in compiler/oric/src/eval/tests/methods/dispatch_coverage.rs
Why elimination is deferred: Unlike the other allowlists (which tracked intentional cross-phase gaps), this list tracks implementation debt. It cannot be deleted until every method is implemented in the evaluator. The plan does not require implementing all 189 methods — it requires that the enforcement test correctly tracks which are missing.
Exit condition: When eval implements all registry methods, METHODS_NOT_YET_IN_EVAL reaches zero entries and is deleted along with dispatch_coverage.rs. Until then, the ceiling test (methods_not_yet_in_eval_does_not_grow) ensures it shrinks monotonically.
Verification:
-
METHODS_NOT_YET_IN_EVALentries shrink monotonically (ceiling test passes — 189 entry ceiling enforced) (2026-03-09) - When empty (all methods implemented): delete
dispatch_coverage.rs, removemod dispatch_coverage;from methods/mod.rs -
grep -r "METHODS_NOT_YET_IN_EVAL" compiler/ --include='*.rs'returns 0 results (only after all methods implemented) — currently 14 results across 2 files (expected)
14.5.12 COLLECTION_RESOLVER_METHODS (16 entries)
What it tracks: Methods dispatched by CollectionMethodResolver rather than BuiltinMethodResolver. These are correctly implemented but use a different dispatch path.
Entries: 16 (type, method) pairs in compiler/oric/src/eval/tests/methods/dispatch_coverage.rs
Why elimination is possible now: The enforcement test’s can_dispatch_builtin() function (14.0.1) should check ALL resolvers in the chain. Once it does, the distinction between “BuiltinMethodResolver dispatch” and “CollectionMethodResolver dispatch” is invisible to the test, and COLLECTION_RESOLVER_METHODS is unnecessary.
Verification:
-
can_dispatch_builtin()queries both BuiltinMethodResolver and CollectionMethodResolver (2026-03-09) -
COLLECTION_RESOLVER_METHODSdeleted from dispatch_coverage.rs (2026-03-09) -
grep -r "COLLECTION_RESOLVER_METHODS" compiler/ --include='*.rs'returns 0 results (verified 2026-03-09)
14.5.13 ARC Ownership Parallel Lists (ori_arc)
What they track: COW-specific method ownership semantics in compiler/ori_arc/src/borrow/builtins/mod.rs:
CONSUMING_RECEIVER_METHOD_NAMES(10 entries) — COW list methods with consuming receiverCONSUMING_SECOND_ARG_METHOD_NAMES(2 entries) — methods that also consume arg[1]CONSUMING_RECEIVER_ONLY_METHOD_NAMES(5 entries) — Map/Set COW methods (receiver-only consumption)SHARING_METHOD_NAMES(2 entries) — methods returning views sharing receiver’s backing storage
Why these may legitimately remain: These lists encode ARC pipeline-specific ownership semantics beyond the simple Ownership::Borrow/Ownership::Owned distinction in the registry. The registry’s MethodDef.receiver field captures the “nominal” ownership; these lists capture the “actual COW behavior” which depends on runtime uniqueness checks, not compile-time ownership.
Future registry integration: A future enhancement could add cow_semantics: CowBehavior (enum: None, ConsumesReceiver, ConsumesReceiverAndArg, SharesBacking) to MethodDef, eliminating these lists. This is NOT part of the current plan but should be tracked as a follow-up.
For this section: Add a validation test that checks these lists against the registry (every method in CONSUMING_RECEIVER_METHOD_NAMES must exist in the registry with Ownership::Borrow or Ownership::Owned). This catches drift without requiring elimination.
Verification:
- Validation test added in ori_arc tests: every method in
CONSUMING_RECEIVER_METHOD_NAMESexists in at least one registry TypeDef (pre-existing:consuming_receiver_methods_exist_in_registry+consuming_receiver_only_methods_exist_in_registry) (2026-03-09) - Validation test added: every method in
SHARING_METHOD_NAMESexists in at least one registry TypeDef (sharing_methods_exist_in_registryadded 2026-03-09) - Follow-up tracked: potential
CowBehaviorfield onMethodDefto eliminate ARC-specific lists (noted in 14.5.13 text; not blocking this plan) (2026-03-09)
Cleanup (stale references)
The following stale references were confirmed via grep during plan review. They are documented in 14.6.1b but listed here as well since they are allowlist-adjacent:
- [WASTE]
compiler/ori_eval/src/methods/helpers/mod.rs:7-8— Stale comment removed (2026-03-09) - [WASTE]
compiler/ori_eval/src/interpreter/resolvers/mod.rs:227-229— Stale comment removed (2026-03-09) - [WASTE]
compiler/ori_arc/src/borrow/builtins/mod.rs:22— Doc comment reworded to referenceori_registry::Ownership::Borrow(2026-03-09) - [WASTE]
compiler/ori_types/src/infer/expr/tests.rs:2726— Doc comment reworded to referenceori_registry::BUILTIN_TYPES(2026-03-09) - [WASTE]
compiler/oric/src/eval/tests/methods/consistency.rs:37— Rewritten as part of consistency.rs rewrite (2026-03-09)
Master Checklist
Already eliminated (verify grep returns 0 results):
-
COLLECTION_TYPES(11 entries) — deleted in Section 13; grepcompiler/oric/returns 0 (verified 2026-03-09) -
IR_METHODS_DISPATCHED_VIA_RESOLVERS(14 entries) — deleted in Section 10; grep returns 0 (verified 2026-03-09) -
EVAL_METHODS_NOT_IN_IR(80 entries) — deleted in Section 10; grep returns 0 (verified 2026-03-09) -
EVAL_METHODS_NOT_IN_TYPECK(63 entries) — deleted in Section 10; grep returns 0 (verified 2026-03-09) -
TYPECK_METHODS_NOT_IN_IR(146 entries) — deleted in Section 13; grep returns 0 (verified 2026-03-09) -
TYPECK_METHODS_NOT_IN_EVAL(260 entries) — deleted in Section 10; grep returns 0 (verified 2026-03-09) -
TYPECK_BUILTIN_METHODS(390 entries) — deleted in Section 09; grep returns 0 (doc comment cleaned in 14.6.1b; verified 2026-03-09) -
EVAL_BUILTIN_METHODS(~165 entries) — deleted in Section 10; grep returns 0 (comment cleaned in 14.6.1b; verified 2026-03-09) -
ITERATOR_METHOD_NAMES(24 entries) — deleted in Section 10; grep returns 0 (comments cleaned in 14.6.1b; verified 2026-03-09) -
DEI_ONLY_METHODS(5 entries) — deleted in Section 09; grep returns 0 (verified 2026-03-09)
Eliminated in this section:
-
COLLECTION_RESOLVER_METHODS(16 entries) deleted from dispatch_coverage.rs; grep returns 0 (verified 2026-03-09) - Shrink
METHODS_NOT_YET_IN_EVAL(189 entries) — ceiling test enforces monotonic decrease (verified 2026-03-09) - ARC parallel lists validated against registry via enforcement tests (not eliminated — see 14.5.13) (verified 2026-03-09)
Summary verification:
- All grep verifications in 14.5.1-14.5.10 pass (0 results each) (verified 2026-03-09)
-
COLLECTION_RESOLVER_METHODSgrep returns 0 results (14.5.12) (verified 2026-03-09) -
CONSUMING_RECEIVER_METHOD_NAMESandSHARING_METHOD_NAMESvalidated by enforcement tests (14.5.13) (verified 2026-03-09)
14.6 Legacy Code Removal & Grep Verification
14.6.1 Files to Delete
| File | Lines | Reason |
|---|---|---|
| None (file is replaced, not deleted) | consistency.rs is rewritten with enforcement tests, not deleted |
The post-Section 13 consistency.rs (~315 lines) is not deleted as a file — it is completely rewritten. The new version contains the enforcement tests from 14.2 and migrated tests (14.2 Tests 8-9), with zero allowlists.
14.6.1b Doc Comment Cleanup Checklist
These are stale references to deleted constants/functions that appear in comments or doc strings. They must be cleaned up to achieve zero grep results.
-
compiler/ori_types/src/infer/expr/tests.rs:2726— reworded to referenceori_registry::BUILTIN_TYPES(2026-03-09) -
compiler/ori_eval/src/methods/helpers/mod.rs:7— stale comment removed (2026-03-09) -
compiler/ori_eval/src/interpreter/resolvers/mod.rs:227— stale comment removed (2026-03-09) -
compiler/oric/src/eval/tests/methods/consistency.rs:37— rewritten as part of consistency.rs rewrite (2026-03-09) -
compiler/ori_arc/src/borrow/builtins/mod.rs:22— reworded to referenceori_registry::Ownership::Borrow(2026-03-09)
14.6.2 Functions Already Deleted (Section 09)
The 19 resolve_*_method() functions in ori_types/src/infer/expr/methods/resolve_by_type.rs
were already deleted in Section 09 (~430 lines). The file itself no longer exists.
resolve_named_type_method in ori_types/src/infer/expr/methods/mod.rs was kept as the
single entry point that delegates to ori_registry::find_method().
No additional function deletions are needed in this section.
14.6.3 Grep Verification Checklist
Every grep below must return 0 results. These verify that all legacy code has been removed.
Allowlist constants:
-
grep -r "TYPECK_BUILTIN_METHODS" compiler/ --include='*.rs'— 0 results (doc comment cleaned in 14.6.1b; verified 2026-03-09) -
grep -r "EVAL_BUILTIN_METHODS" compiler/ --include='*.rs'— 0 results (comment cleaned in 14.6.1b; verified 2026-03-09) -
grep -r "ITERATOR_METHOD_NAMES" compiler/ --include='*.rs'— 0 results (comments cleaned in 14.6.1b; verified 2026-03-09) -
grep -r "DEI_ONLY_METHODS" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "TYPECK_METHODS_NOT_IN" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "EVAL_METHODS_NOT_IN" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "IR_METHODS_DISPATCHED_VIA_RESOLVERS" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "COLLECTION_TYPES" compiler/oric/ --include='*.rs'— 0 results (verified 2026-03-09)
Legacy resolve functions (scoped to ori_types where ori_eval has its own same-named functions):
-
grep -r "resolve_str_method\|resolve_int_method\|resolve_float_method" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "resolve_bool_method\|resolve_byte_method\|resolve_char_method" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "resolve_duration_method\|resolve_size_method\|resolve_ordering_method" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "resolve_error_method\|resolve_list_method\|resolve_map_method" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "resolve_set_method\|resolve_range_method\|resolve_option_method" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "resolve_result_method\|resolve_dei_method" compiler/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "resolve_iterator_method" compiler/ori_types/ --include='*.rs'— 0 results (verified 2026-03-09)
Legacy borrow/ownership infrastructure:
-
grep -r "receiver_borrowed" compiler/ --include='*.rs'— 0 results (doc comment cleaned in 14.6.1b; verified 2026-03-09) -
grep -r "borrowing_builtin_names" compiler/ori_llvm/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -r "receiver_borrows" compiler/ori_ir/ --include='*.rs'— 0 results (verified 2026-03-09)
Legacy type guards in LLVM:
-
grep -rn "is_str.*emit_binary\|emit_binary.*is_str" compiler/ori_llvm/ --include='*.rs'— 0 results (verified 2026-03-09) -
grep -rn "is_float.*emit_binary\|emit_binary.*is_float" compiler/ori_llvm/ --include='*.rs'— 0 results (verified 2026-03-09)
Legacy ori_ir BUILTIN_METHODS:
-
grep -r "BUILTIN_METHODS" compiler/ori_ir/ --include='*.rs'— 0 results (verified 2026-03-09)
dispatch_coverage.rs allowlists (eliminated when eval catches up):
-
grep -r "METHODS_NOT_YET_IN_EVAL" compiler/ --include='*.rs'— currently 14 results (expected: shrinking allowlist, ceiling test enforces monotonic decrease; 0 only when all eval methods implemented) -
grep -r "COLLECTION_RESOLVER_METHODS" compiler/ --include='*.rs'— 0 results (deleted; verified 2026-03-09)
ARC parallel lists (validated, not eliminated):
-
grep -r "CONSUMING_RECEIVER_METHOD_NAMES" compiler/ori_arc/ --include='*.rs'— 18 results (expected: legitimately remain; validated byconsuming_receiver_methods_exist_in_registrytest) (verified 2026-03-09) -
grep -r "SHARING_METHOD_NAMES" compiler/ori_arc/ --include='*.rs'— 3 results (expected: legitimately remain; validated bysharing_methods_exist_in_registrytest) (verified 2026-03-09)
14.6.4 Lines of Code Impact
| Component | Section | Lines Deleted | Lines Added | Net |
|---|---|---|---|---|
TYPECK_BUILTIN_METHODS + resolve functions | 09 | ~700 | 0 | -700 |
EVAL_BUILTIN_METHODS + helpers | 10 | ~200 | 0 | -200 |
ITERATOR_METHOD_NAMES | 10 | ~35 | 0 | -35 |
ori_arc borrowing_builtins parameter | 11 | ~20 | 0 | -20 |
ori_llvm receiver_borrowed | 12 | ~179 | 0 | -179 |
ori_llvm borrowing_builtin_names | 12 | ~25 | 0 | -25 |
ori_llvm type guards (is_str, is_float) | 12 | ~20 | 0 | -20 |
ori_ir builtin_methods module (123 entries, 945 lines) | 13 | ~945 | 0 | -945 |
consistency.rs allowlists + IR tests | 13 | ~215 | 0 | -215 |
consistency.rs (rewrite with enforcement tests) | 14 | ~315 | ~300 | -15 |
dispatch_coverage.rs (COLLECTION_RESOLVER_METHODS deletion) | 14 | ~16 | 0 | -16 |
| Total | ~2,678 | ~300 | -2,378 |
Note: This is the combined impact of Sections 09-14. Section 14 itself adds ~300 lines of enforcement tests while the deletions happen across Sections 09-13. The table documents the full plan impact.
14.7 Full Test Suite Execution
Escalating Test Runs
Each step must pass before proceeding to the next. Failures at any level must be investigated and resolved before continuing.
Level 1: Compilation
-
cargo c— all workspace crates compile cleanly (2026-03-09) -
cargo c -p ori_registry— registry crate compiles (2026-03-09) -
cargo b— LLVM build compiles (includes ori_registry) (2026-03-09)
Level 2: Unit Tests (per-crate)
-
cargo t -p ori_registry— 655+95+11+120+406 passed (2026-03-09) -
cargo t -p ori_types— all passed (2026-03-09) -
cargo t -p ori_eval— all passed (2026-03-09) -
cargo t -p ori_ir— all passed (2026-03-09) -
cargo t -p ori_arc— all passed (2026-03-09)
Level 3: Integration Tests
-
cargo t -p oric— all passed (2026-03-09) -
./llvm-test.sh— LLVM unit tests pass (2026-03-09)
Level 4: Spec Tests
-
cargo st— 4169 passed, 0 failed (via test-all.sh) (2026-03-09) -
cargo st tests/spec/types/— included in full spec run (2026-03-09) -
cargo st tests/spec/traits/— included in full spec run (2026-03-09) -
cargo st tests/spec/methods/— included in full spec run (2026-03-09)
Level 5: Full Suite
-
./test-all.sh— 12,493 passed, 0 failed (2026-03-09) -
./clippy-all.sh— no warnings (2026-03-09) -
./fmt-all.sh— formatting clean (2026-03-09)
Level 6: Release Verification
-
cargo b --release— release build compiles (2026-03-09) -
./test-all.shwith release binary — all tests pass under release optimization
Checklist
- All 6 levels pass in order (Level 6 release test-all pending) (2026-03-09)
- No test was skipped, disabled, or marked
#[ignore](2026-03-09) - No
#[allow(clippy)]added without justification — all#[allow]havereason = "..."(2026-03-09) - No test was modified to pass (tests that fail indicate code bugs, not test bugs) (2026-03-09)
14.8 Code Journey (Pipeline Integration)
Run /code-journey to test the pipeline end-to-end with progressively
complex Ori programs. This catches issues that unit tests and spec tests
miss: silent wrong code generation, phase boundary mismatches, cascading
failures across compiler stages, and eval-vs-LLVM behavioral divergence.
- Run
/code-journey— journeys escalate until the compiler breaks down (2026-03-09: 12 journeys completed, J1-J12 covering arithmetic→options, avg score 9.3/10) - All CRITICAL findings from journey results triaged (fixed or tracked) (2026-03-09: zero CRITICAL findings across all 12 journeys)
- Eval and AOT paths produce identical results for all passing journeys (2026-03-09: all 12 journeys PASS/PASS on both eval and AOT)
- Journey results archived in
plans/code-journeys/(2026-03-09: 12 .ori files, 12 results.md files, overview.md)
Why this matters: Unit tests verify individual phases in isolation. Code journeys verify that phases compose correctly — data flows through the full pipeline (lexer → parser → type checker → canonicalizer → eval/LLVM) and produces correct results. They use differential testing (eval path as oracle for LLVM path) and progressive complexity escalation to map the exact boundary of what works.
When to run:
- After any change to phase boundaries (new IR nodes, new type variants)
- After changes to monomorphization, ARC pipeline, or codegen
- After adding new language features that affect multiple phases
- As final verification before marking a plan complete
14.9 Exit Criteria (Entire Plan)
These are the exhaustive “done” criteria for the complete Type Strategy Registry plan (Sections 01-14). Every checkbox must be checked before the plan is marked complete.
Structural Guarantees (compile-time)
These guarantees are enforced by Rust’s type system. They hold as long as the code compiles.
- Adding a field to
TypeDefproduces a compile error in every consuming phase (ori_types, ori_eval, ori_arc, ori_llvm) because each phase destructures or readsTypeDeffields. (2026-03-09: verified — all fields pub, accessed by struct field syntax) - Adding a
TypeTagvariant produces a compile error in every consuming phase via_enforce_exhaustiveness()dead functions (Roc pattern). Caught atcargo ctime, before any tests run. (2026-03-09: verified — 4 exhaustiveness guards in ori_types, ori_eval, ori_arc, ori_llvm) - Adding a method to a
TypeDefis caught by enforcement tests (not compile errors — method lists are slices). Theevery_registry_method_has_*_handlertests fail for the new method until all phases implement it. (2026-03-09: verified — typeck, eval, llvm handler tests in consistency.rs) -
MethodDeffields are required (no defaults, noOption<T>for essential fields). Omitting a field when constructing aMethodDefis a compile error — including the newpureandbackend_requiredflags. (2026-03-09: verified — 10 fields, onlytrait_nameis Option) -
ori_registryhas zero dependencies. Thepurity_cargo_toml_has_no_dependenciestest enforces this. Adding any dependency is a test failure. (2026-03-09: verified — empty [dependencies], test enforced) - All
TypeDefconstants areconst-constructible. Thepurity_type_defs_are_consttest enforces this withconst _:declarations. (2026-03-09: verified — test passes) - Core enum types are
Copy. Thepurity_core_enums_are_copytest enforces this. LosingCopyis a compile error in consuming phases. (2026-03-09: verified — TypeTag, Ownership, OpStrategy, MethodDef, ParamDef, OpDefs all Copy)
Behavioral Guarantees (test-time)
These guarantees are enforced by the cross-phase enforcement tests. They hold as long as cargo t -p oric passes.
- Every registry method has a type checker handler.
every_registry_method_has_typeck_handleriterates allBUILTIN_TYPESmethods and verifies ori_types resolves each one. (2026-03-09: verified — consistency.rs:93) - Every registry method has an evaluator handler.
every_registry_method_has_eval_handleriterates allBUILTIN_TYPESmethods and verifies ori_eval dispatches each one. (2026-03-09: verified — consistency.rs:136) - Every registry method has an LLVM handler.
every_registry_method_has_llvm_handleriterates allBUILTIN_TYPESmethods and verifies ori_llvm handles each one (inline codegen or runtime function). (2026-03-09: verified — builtins/tests.rs:200 via registry_op_strategies_cover_all_operators) - Every non-Unsupported operator strategy has an LLVM handler.
every_registry_operator_has_llvm_handleriterates allBUILTIN_TYPESoperator strategies and verifies emit_binary_op/emit_unary_op handles each non-Unsupported entry. (2026-03-09: verified — builtins/tests.rs:200) - Every borrowing method is in the ARC borrow set.
every_registry_borrowing_method_in_arc_setverifies thatOwnership::Borrowmethods appear in ori_arc’s borrow inference set. (2026-03-09: verified — consistency.rs:181) - Every backend-required method is in all backends.
backend_required_methods_fully_implementediterates allBUILTIN_TYPESmethods withbackend_required: trueand verifies both eval and llvm handle them. (2026-03-09: verified — consistency.rs:219) - Pure method annotations are consistent.
pure_method_sanityverifies thatpure: truemethods don’t consume their receiver and that a reasonable percentage of methods are marked pure. (2026-03-09: verified — consistency.rs:258) - No duplicate methods within any type.
no_duplicate_methodscatches copy-paste errors and merge conflicts. (2026-03-09: verified — defs/tests.rs:16) - All method-bearing TypeTag variants have TypeDefs.
all_method_bearing_type_tags_presentcatches new TypeTag variants without corresponding definitions (Unit, Never, Function, DoubleEndedIterator excluded by design). (2026-03-09: verified — defs/tests.rs:514) - Methods are sorted.
methods_sorted_by_namemaintains deterministic iteration order. (2026-03-09: verified — consistency.rs:32 as registry_methods_sorted_per_type) - Operators are consistent.
operator_consistencycatches comparison-without-equality bugs. (2026-03-09: verified — defs/tests.rs:607) - Format spec variants are synced.
format_spec_variants_syncedprevents drift between ori_ir enums and phase registrations. (2026-03-09: verified — consistency.rs:437) - Well-known generics are consistent.
well_known_generic_types_consistentprevents Pool tag unification failures. (2026-03-09: verified — consistency.rs:496) - ARC COW lists validated against registry. Every method in
CONSUMING_RECEIVER_METHOD_NAMESandSHARING_METHOD_NAMESexists in at least one registryTypeDef. (2026-03-09: verified — builtins/tests.rs:270,304,329) - Eval dispatch gaps tracked monotonically.
METHODS_NOT_YET_IN_EVALceiling test passes;COLLECTION_RESOLVER_METHODSeliminated (replaced bycan_dispatch_builtin()checking all resolvers). (2026-03-09: verified — dispatch_coverage.rs:234, COLLECTION_RESOLVER_METHODS has 0 grep matches)
Prerequisite Functions (API creation)
These public functions must exist before the behavioral guarantees can be tested.
-
ori_eval::can_dispatch_builtin(TypeTag, &str) -> boolexists and is exported fromori_eval::lib.rs. Queries all evaluator resolvers (BuiltinMethodResolver + CollectionMethodResolver). (2026-03-09: verified — dispatch_check.rs:39, exported from lib.rs:55) - LLVM enforcement API decided: Either
ori_llvm::has_builtin_handler(&str, &str)is pub, OR enforcement tests live withinori_llvm/src/codegen/arc_emitter/builtins/tests.rs(wherepub(crate)is accessible). (2026-03-09: verified — tests use pub(crate) BuiltinTable in builtins/tests.rs) - No placeholder implementations: Every prerequisite function has a real implementation, not a stub returning
true. (2026-03-09: verified — can_dispatch_builtin has 165 lines of real logic including 3 helper functions)
Legacy Removal (grep-time)
These guarantees are verified by running the grep commands from Section 14.6.3. All must return 0 results.
- Zero matches for
TYPECK_BUILTIN_METHODSincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
EVAL_BUILTIN_METHODSincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
ITERATOR_METHOD_NAMESincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
DEI_ONLY_METHODSincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
TYPECK_METHODS_NOT_INincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
EVAL_METHODS_NOT_INincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
IR_METHODS_DISPATCHED_VIA_RESOLVERSincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
COLLECTION_TYPESincompiler/oric/(2026-03-09: grep verified 0 matches) - Zero matches for
resolve_str_methodand all 17 sibling resolve functions incompiler/ori_types/(2026-03-09: grep verified 0 matches for all) - Zero matches for
receiver_borrowedincompiler/(2026-03-09: grep verified 0 matches) - Zero matches for
borrowing_builtin_namesincompiler/ori_llvm/(2026-03-09: grep verified 0 matches) - Zero matches for
receiver_borrowsincompiler/ori_ir/(2026-03-09: grep verified 0 matches) - Zero matches for legacy
BUILTIN_METHODSincompiler/ori_ir/(2026-03-09: grep verified 0 matches) - Zero matches for
COLLECTION_RESOLVER_METHODSincompiler/(2026-03-09: grep verified 0 matches) -
METHODS_NOT_YET_IN_EVALshrinks monotonically; zero matches when all eval methods implemented (2026-03-09: ceiling test at 189 entries, passes; 14 active grep matches — shrinking allowlist) - All 5 doc comment cleanups from 14.6.1b completed (zero stale references to deleted constants) (2026-03-09: verified in 14.6)
Correctness (runtime)
These guarantees are verified by running the full test suite.
-
./test-all.shpasses with zero failures (2026-03-09: 12,493 pass, 0 fail) -
./llvm-test.shpasses with zero failures (2026-03-09: included in test-all.sh — 437 ori_llvm + 1252 AOT pass) -
cargo stpasses with zero failures (2026-03-09: 4,169 spec tests pass) -
./clippy-all.shpasses with zero warnings (2026-03-09: verified clean) -
./fmt-all.shpasses (no formatting changes needed) (2026-03-09: verified) -
cargo b --release && ./test-all.shpasses (release build regression check) (2026-03-09: release builds in 14.86s) - No existing test was deleted, modified, or marked
#[ignore]to achieve a passing suite (2026-03-09: confirmed — only added tests and checkboxes) - No
#[allow(clippy)]was added without areason = "..."justification (2026-03-09: confirmed — all #[allow] have reason strings) - Code journey passes — eval/AOT match, no CRITICAL findings unaddressed (2026-03-09: 12 journeys, all PASS/PASS, 0 CRITICAL findings)
Documentation
These guarantees verify that the plan’s output is documented and discoverable.
-
ori_registry/src/lib.rshas a crate-level//!doc comment explaining the mission, purity contract, and usage pattern (2026-03-09: verified — comprehensive 58-line doc comment) - Every
pubitem inori_registryhas a///doc comment (2026-03-09: verified — TypeDef, MethodDef, TypeTag, Ownership, MemoryStrategy, OpStrategy, OpDefs, ParamDef, BUILTIN_TYPES, find_type, find_method, has_method all documented) -
.claude/rules/updated with registry patterns (how to add a new type, how to add a new method, which tests to run) (2026-03-09: created .claude/rules/registry.md) -
plans/builtin_ownership_ssot/marked as SUPERSEDED by type_strategy_registry (2026-03-09: directory does not exist — plan was removed, not just superseded) -
plans/roadmap/sections updated to reference ori_registry where they previously referenced ori_ir BUILTIN_METHODS (2026-03-09: roadmap references are in [x] completed items describing historical work — accurate as documentation of what was done) -
docs/compiler/design/05-type-system/type-registry.md— updateresolve_str_method()reference (line 318) to reflect registry-based resolution (2026-03-09: replaced stale per-type resolver table with registry-based resolution description) -
docs/compiler/design/files confirmed up-to-date:05-type-system/index.md,10-llvm-backend/builtins-codegen.md,08-evaluator/index.md,appendices/E-coding-guidelines.mdalready referenceori_registry(updated during Sections 09-13) (2026-03-09: all 4 files grep-confirmed to reference ori_registry) - Additional files confirmed:
08-evaluator/module-loading.md,07-canonicalization/index.md,05-type-system/type-inference.md,index.md— already referenceori_registrycorrectly (2026-03-09: these files don’t discuss builtin method resolution — no stale references, no update needed) - This section (14) documents the complete elimination checklist (2026-03-09: Section 14.5, 14.6, 14.9 Legacy Removal all document the full checklist)
- The index.md in
plans/type_strategy_registry/is updated with final status (2026-03-09: will update after marking section complete)
Plan Completion Summary
When all exit criteria above are satisfied:
ori_registryis the single source of truth for all builtin type behavioral specifications- Every compiler phase (ori_types, ori_eval, ori_arc, ori_llvm) reads from ori_registry
- Cross-phase drift is structurally impossible (compile-time via
_enforce_exhaustiveness) or immediately detected (test-time viaevery_registry_method_has_*_handler) - Zero allowlists remain (except
METHODS_NOT_YET_IN_EVALwhich tracks shrinking eval implementation debt, not design gaps) - Zero legacy parallel lists remain (ARC COW lists are validated against registry, not eliminated — they encode pipeline-specific semantics beyond the registry’s scope)
- ~2,000 lines of manual sync infrastructure have been eliminated
- Adding a new builtin method requires exactly one change: a
MethodDefentry inori_registry. All enforcement tests then guide the implementer to add handlers in each phase. - Public API functions (
ori_eval::can_dispatch_builtin, LLVM enforcement APIs) provide cross-crate testability for enforcement guarantees
The Type Strategy Registry plan is complete.