Skip to content

Commit e3c4b11

Browse files
committed
IxVM kernel: document k_infer_only safety invariant + planned hint shape
The `k_infer_only` section header skipped over the fact that the function is only sound on well-typed inputs (since it drops `k_check(a, dom)` on `App`, `k_ensure_sort(ty)` on `Lam`, val/ty checks on `Let`). Spell out: * The invariant — only call on terms produced by `whnf` of a well-typed term, never on arbitrary inputs. * Why the current sites (`try_proof_irrel`, `is_prop_type`, `try_unit_like`) respect it. * The planned non-deterministic-hint dispatch (`Hint::{None, KInfer, KInferOnly}`) that lets us share `k_infer`'s memo where a hit already exists instead of paying the parallel `infer_only` memo cost.
1 parent 07712ff commit e3c4b11

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

Ix/IxVM/Kernel/Infer.lean

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,43 @@ def infer := ⟦
191191
-- `k_infer` (parity with Rust's separate infer_cache / infer_only_cache).
192192
-- Used at try_proof_irrel / is_prop_type / try_unit_like where only the
193193
-- synthesized type is needed.
194+
--
195+
-- ## Safety invariant
196+
--
197+
-- `k_infer_only` is NOT safe to call on an arbitrary term — only on terms
198+
-- already known to be well-typed (i.e., terms that would have passed
199+
-- `k_infer`). The reason: our overall invariant is that evaluation only
200+
-- happens after typechecking. `k_infer_only` evaluates types (e.g. the
201+
-- substitution `B[a/x]` on an `App` arm) WITHOUT first checking that the
202+
-- substituted-in argument `a` has the proper type (the dropped
203+
-- `k_check(a, dom)`). On an untyped or ill-typed term, that substitution
204+
-- can take us out of the well-typed fragment, after which subsequent
205+
-- `whnf` / `def_eq` work is unsound.
206+
--
207+
-- The current call sites (`try_proof_irrel`, `is_prop_type`,
208+
-- `try_unit_like`) honor this invariant because they hand `k_infer_only`
209+
-- a term obtained by `whnf`-ing a well-typed input. `whnf` of a
210+
-- well-typed term yields a structurally different but still well-typed
211+
-- term, so the no-checks shortcut is sound there even though the result
212+
-- term itself was never the direct subject of `k_infer`.
213+
--
214+
-- ## Future: shared memo via non-deterministic hint
215+
--
216+
-- `k_infer_only`'s separate memo (parity with Rust) means an `infer_only`
217+
-- call cannot reuse an existing `k_infer` hit on the same input. A
218+
-- planned improvement: at each `try_proof_irrel` / `try_unit_like` site,
219+
-- the prover supplies a hint `enum Hint { None, KInfer, KInferOnly }`:
220+
-- 1. `None` — term is not unit-like and not a proof; skip both
221+
-- and fall through to deep `def_eq`.
222+
-- 2. `KInfer` — `k_infer`'s memo is a hit on this input; call
223+
-- `k_infer` (cheap, reuses the cached row) and
224+
-- check the result is a proof / unit-like.
225+
-- 3. `KInferOnly` — `k_infer`'s memo would miss; call `k_infer_only`
226+
-- (cheaper than a fresh `k_infer`) and check.
227+
-- Dispatching on the hint with a `match` lets us share `k_infer`'s memo
228+
-- where available and fall back to `k_infer_only` only when the cheaper
229+
-- path won't pay off, instead of unconditionally paying the parallel
230+
-- `infer_only` memo cost.
194231
-- ============================================================================
195232
fn k_infer_only(e: KExpr, types: List‹KExpr›,
196233
top: List‹&KConstantInfo›, addrs: List‹Addr›) -> KExpr {

0 commit comments

Comments
 (0)