Allow user-specified closure pre/post via closure! macro#189
Draft
coord-e wants to merge 1 commit into
Draft
Conversation
Closure pre-/post-conditions were always inferred as predicate-variable
templates. Add `thrust_macros::closure!(requires(..), ensures(..), |x: T| -> R
{ .. })` so a user can pin them explicitly, mirroring Prusti's `closure!`.
The macro rewrites the closure body to carry `#[thrust::formula_fn]` companions
plus `#[thrust::requires_path]` / `#[thrust::ensures_path]` path statements — the
same markers the plugin already reads for named-fn specs. Each clause is
optional; an omitted side stays inferred (a pvar).
Plugin changes:
- `expected_ty` shifts a closure formula's argument indices by one to account
for the environment parameter that leads a closure's `FunctionType` params.
- `refine_local_defs` registers all formula fns in a pre-pass so a closure's
spec companions (nested in its own body) resolve regardless of `mir_keys`
order.
Adds passing/failing UI test pairs for both-clause and ensures-only closures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019mT8dkzKVXeCA4DhFTn9wj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closure pre-/post-conditions were always inferred as predicate-variable (pvar) templates. This adds a way to pin them explicitly:
Each clause is optional — omit one and that side stays inferred (a pvar). The surface mirrors Prusti's
closure!, which shares thrust's constraint of per-item proc-macros on stable Rust (Verus-style in-signature clauses need a whole-program macro; Creusot-style attributes-on-closures need whole-body rewriting — neither fits).How it works
Maximal reuse of the existing named-fn spec machinery:
thrust-macros/src/closure.rs(new) — rewrites the closure body to carry#[thrust::formula_fn]companions plus#[thrust::requires_path]/#[thrust::ensures_path]path statements, the same markers the plugin already reads for named functions. ReusesFormulaFnTypeLoweringandformula::expand. The closure header supplies parameter/return types, so nothing is restated.src/analyze/local_def.rs—expected_tyshifts a closure formula's argument indices by one to account for the environment parameter that leads a closure'sFunctionTypeparams ([env, arg1, .., argN]). This is the core plugin change; the existing installation path (param_refinement/ret_refinement) then swaps in the user formula in place of the inferred pvar.src/analyze/crate_.rs—refine_local_defsregisters every formula fn in a pre-pass so a closure's spec companions (nested inside the closure body) resolve regardless of the ordermir_keysyields them.No change needed to
FunctionType(pre/post are already the params'/return refinements) or topre!/post!(callers still reference the now-fixed spec the same way). The injected path statements analyze cleanly without special skipping.Tests
Adds passing/failing UI test pairs following the repo convention (
//@check-passvs//@error-in-other-file: Unsat):closure_requires_ensures— a closure with both clauses, plus a caller relying on the fixed spec.closure_ensures_only— onlyensuresgiven;requiresstays inferred.Verified end-to-end with
z3as the CHC solver. The full UI suite shows zero regressions — the failing set is identical to the base tree's pre-existing solver-limitation failures.Out of scope (follow-ups)
Selfcontext threading (theinvariant_contextmachinery): closures in generic contexts referring to generic-/Self-typed values aren't supported yet, matchinginvariant!'s initial capability.FnMutbefore/after) in a closure spec — the env param plumbing exists, but the macro surface for it is left as a follow-up.🤖 Generated with Claude Code
Generated by Claude Code