Expand description
Shared proof-mode recursion analysis.
Classifies each recursive pure fn into a RecursionPlan that tells
the proof backends (Lean, Dafny) how to emit a fuel-guarded helper
plus a wrapper with an appropriate fuel metric. The same classifier
feeds both backends so supported shapes stay consistent.
Emission is backend-specific (syntax, termination-proof mechanism, default-value for fuel exhaustion), but the recognition pass and the AST transform that rewrites recursive calls into helper calls are shared.
Re-exports§
pub use detect::analyze_plans_in_scope;pub use crate::codegen::common::substitute_ident_in_expr;
Modules§
- detect
- Proof-mode recursion classifier.
Structs§
- Proof
Mode Issue - Classifier-side diagnostic: a recursive fn whose shape falls
outside every supported pattern.
proof_lower::populate_fn_ contractstranslates these intoProofIR.unclassified_fns— consumers should readctx.proof_ir.unclassified_fns(typedVec<UnclassifiedFn>) instead of reaching for this type.
Enums§
- Recursion
Plan - Classification for a single recursive fn (or a whole mutual-recursion SCC, in which case every fn in the SCC gets its own plan from the same family).
Constants§
- OMEGA_
PROOF_ SENTINEL - Sentinel identifier injected as an extra synthetic argument at every
recursive callsite inside an
IntCountdownGuardedbody. Lean’s expr emitter recognises this name and renders it as(by omega); Dafny’s codegen never sees it because Dafny discharges preconditions via auto-inference at the existing fn-def emit path.
Functions§
- expr_
references_ ident - True iff
expr(recursively) mentionsnameas anIdent/Resolvedreference. Used by the caller-guard extractor to filter out enclosing predicates that don’t constrain the variable passed at the countdown-param position — those predicates name caller locals that aren’t in scope inside the callee. - flip_
comparison_ binop - Flip a comparison
BinOpto its logical negation so a caller’smatch (a OP b) { false -> ... }arm normalises into a positive predicate. ReturnsNonefor non-comparison operators (Add,Sub, etc.) — those can’t legally land as amatch … { Bool -> ... }subject anyway. Used by the issue-84 caller-guard extractor so every collected predicate is the sameSpanned<Expr>shape opaque types and verifywhenalready use, avoiding a parallel(expr, negated)representation. - fuel_
helper_ name - Canonical suffix for a fuel-guarded helper fn. Deliberately contains only lowercase ASCII + underscores so both Lean and Dafny accept it as an identifier without renaming.
- native_
aux_ name - Suffix for the native-emit auxiliary fn that carries the explicit
precondition parameter. Mirrors
fuel_helper_name’s ASCII-only shape so Lean accepts it verbatim. - rewrite_
native_ guarded_ calls_ body - Body-level wrapper around
rewrite_native_guarded_calls_expr. - rewrite_
native_ guarded_ calls_ expr - Walk
exprand rewrite every recursive call tofn_nameinto a call toaux_namecarrying an extraOMEGA_PROOF_SENTINELident at the end of the argument list. Used by the proof-modeIntCountdownGuardedlowering — the synthetic argument lets Lean discharge the precondition at every recursive site without touching the original Aver source. - rewrite_
native_ guarded_ calls_ resolved_ expr ResolvedExprmirror ofrewrite_native_guarded_calls_expr. The proof-modeIntCountdownGuardedshape stores the rewritten arms onProofIR(now resolved), so the rewriter that liftsfn(args)toaux_name(args, OMEGA_PROOF_SENTINEL)works in resolved space too.- rewrite_
recursive_ calls_ body - Body-level wrapper around
rewrite_recursive_calls_expr. - rewrite_
recursive_ calls_ expr - AST transform: walk
exprand replace every recursive call to a fn intargetswithfn__fuel(fuel_var, …args). Inter-fn mutual calls in the same SCC are rewritten identically (the fuel parameter is threaded through the whole group).