Relate OuterFnParam to locals via FnParam::at_here in loop invariants#156
Draft
coord-e wants to merge 2 commits into
Draft
Relate OuterFnParam to locals via FnParam::at_here in loop invariants#156coord-e wants to merge 2 commits into
coord-e wants to merge 2 commits into
Conversation
A user-supplied loop invariant replaces the inferred precondition, so the relationship between a function argument's entry value (`OuterFnParam`) and its current local at the loop header is no longer carried. #150 patched this with an ad-hoc equality on non-flow `OuterFnParam`s; that equality is valid but its behavior implicitly depends on the binding's type/mutability. Replace it with a general mechanism that lets invariants relate the two views explicitly: - `FnParam::at_here()` denotes the argument's current local at the loop header, as opposed to `at_entry()` (its value at function entry, the `OuterFnParam`). `FnParam` itself stays a single value (the entry value); `at_here()` resolves to the argument's separate loop-header local. In the lowered formula, invariant parameter `i` carries its entry value at variable `i` and its "here" value at variable `params.len() + i`; `build_invariant_precondition` wires the latter to `param_of_local`. - `FnParam::is_not_changed()` is `at_entry() == at_here()`. - Referring to a function parameter's local directly (without `FnParam`) in an invariant signature is now an error, since the entry-vs-current choice would be implicit (`self` is exempt: it is loop-carried and denotes its current value). Remove the ad-hoc equality and `is_non_flow_local`. Tests: rewrite the #150 pass/fail to use `is_not_changed()`/`at_here()`; add a mutable-argument pass/fail pair where `at_here()` genuinely differs from `at_entry()`; add a direct-reference error test; update the generic invariant tests that referred to a parameter directly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKHXf7J5gPQ7uHvnehQsTd
`FnParam::at_here` differs from `at_entry` whenever the argument is flow-bound. Cover the mutable-borrow form explicitly (the argument is `&mut`-borrowed in the loop body, not just reassigned) with a pass/fail pair, alongside the existing reassignment pair. Also add the missing pass counterpart for the direct-reference test so the new tests follow the pass/fail pairing convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKHXf7J5gPQ7uHvnehQsTd
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.
Follow-up to #150.
Problem
A user-supplied loop invariant replaces the inferred precondition, so the relationship between a function argument's entry value (
OuterFnParam) and its current local at the loop header is no longer carried. #150 patched this by adding an ad-hoc equality on non-flowOuterFnParams. That equality is valid (it always holds for non-flow locals), but ad-hoc: its behavior implicitly changes with the binding's type/mutability, and the invariant cannot express both views of the same argument.Fix
Let invariants relate the two views explicitly, instead of relying on an implicit equality:
FnParam::at_here()denotes the argument's current local at the loop header, as opposed toat_entry()(its value at function entry — theOuterFnParam).FnParamitself stays a single value (the entry value, unchanged);at_here()resolves to the argument's separate loop-header local. Concretely, in the lowered formula each invariant parametericarries its entry value at variableiand its "here" value at variableparams.len() + i, andbuild_invariant_preconditionwires the latter toparam_of_local. No tuple/new sort is introduced.FnParam::is_not_changed()isat_entry() == at_here()— the explicit form of what [codex] Preserve outer parameter links in loop invariants #150 asserted implicitly.FnParam) in an invariant signature is now an error, so the entry-vs-current choice is always explicit.selfis exempt: it is loop-carried and denotes its current value.The ad-hoc equality (and
is_non_flow_local) are removed.With this, the #150 case reads:
Tests
All new tests come in pass/fail pairs:
loop_invariant_outer_param— the [codex] Preserve outer parameter links in loop invariants #150 case (immutable argument):is_not_changed()+at_here()dischargesresult == a; droppingis_not_changed()fails.loop_invariant_fn_param_at_here— argument reassigned in the loop (a += 1):at_here()differs fromat_entry();at_entry() <= at_here()dischargesresult >= a, while claimingis_not_changed()correctly fails.loop_invariant_fn_param_at_here_mut_borrow— argument mutably borrowed in the loop (flow-bound:let r = &mut a; *r += 1), the canonical case whereat_here() != at_entry(); same pass/fail shape.loop_invariant_fn_param_direct— referring to a parameter directly is rejected (fail) vs. the same program written withFnParam(pass).FnParam+at_here().Notes
cargo fmt/cargo clippy -D warningsare clean. The full suite passes excepttests/ui/fail/option_map.rs, which hangs in this sandbox only — its PCSat (COAR) solver run does not terminate here, and that test uses no invariants/FnParam, so it is unaffected by this change. Worth a confirming CI run.🤖 Generated with Claude Code
https://claude.ai/code/session_01KKHXf7J5gPQ7uHvnehQsTd