# const-reify Design Document
## Overview
`const-reify` provides a runtime-to-const-generic bridge: given a runtime `u64`
value, it dispatches to a monomorphized function parameterized by a const generic
matching that value. This enables "reification" — lifting runtime data into the
type system.
## Approach
Rather than vtable fabrication (which is fragile and relies on undocumented
compiler internals), we use a **match-table dispatch** strategy:
1. Pre-monomorphize a trait impl for `Modular<N>` for `N` in `0..=255`.
2. At runtime, match the provided `u64` against this table and dispatch to the
correct monomorphization.
3. The dispatch is generated by a macro to avoid writing 256 match arms by hand.
This approach is:
- **Sound**: No transmute, no raw pointer arithmetic, no vtable assumptions.
- **Portable**: Works on any Rust compiler that supports const generics (stable since 1.51).
- **Predictable**: The monomorphization cost is bounded and explicit.
## Why Not Vtable Fabrication?
The original plan called for inspecting and fabricating vtables at runtime.
This approach has severe drawbacks:
1. **Vtable layout is not stabilized.** Rustc makes no guarantees about vtable
structure, entry ordering, or even whether vtables exist in the expected form.
Different optimization levels, LTO settings, and compiler versions can change
the layout.
2. **Transmuting vtable pointers is instant UB.** Creating a trait object from
fabricated pointers violates Rust's aliasing and provenance rules.
3. **No mitigation is reliable.** Compile-time assertions can detect *some*
layout changes, but cannot guarantee soundness. A passing assertion today
does not prevent UB tomorrow.
The match-table approach achieves the same user-facing API with zero unsafe code
in the dispatch path.
## Rustc Version Compatibility
- **Minimum:** Rust 1.75.0 (MSRV of the workspace)
- **Tested on:** Current stable (1.94.0)
- **Const generics:** Stabilized since Rust 1.51.0 for primitive types
No compiler-internal assumptions are made. This crate should work on any
stable Rust >= 1.51.
## Threat Model
| Compiler changes vtable layout | N/A | We don't use vtables |
| Monomorphization explosion | Low | Bounded to 256 variants |
| Value out of supported range | Low | Runtime panic with clear message |
| Const generic type mismatch | None | Enforced by trait bounds |
## Supported Range
Initial implementation: `u64` values in `0..=255`.
This keeps compile times reasonable (256 monomorphizations). The range can be
extended in the future by:
- Increasing the match table (linear compile-time cost)
- Using a two-level dispatch (high byte + low byte) for larger ranges
- Using build-script-generated dispatch tables
## API Surface
```rust
// The core trait
pub trait HasModulus {
fn modulus(&self) -> u64;
}
// Auto-implemented for Modular<N>
pub struct Modular<const N: u64>;
// Safe dispatch
pub fn reify_const<F, R>(val: u64, f: F) -> R
where
F: FnOnce(&dyn HasModulus) -> R;
// Convenience macro
});
```
## Unsafe Code Policy
This crate was originally designated as the "unsafe core" of the workspace.
However, the match-table approach eliminates the need for unsafe code entirely.
The crate now uses `#![deny(unsafe_code)]` like all other crates in the
workspace, except for the test harness in `tests/vtable_inspection.rs` which
documents vtable layout for educational purposes only.