feat: apply @[mvcgen_invariant_type] to Invariant, StringInvariant, StringSliceInvariant (#12880)

This PR applies `@[mvcgen_invariant_type]` to `Std.Do.Invariant` and
removes the hard-coded fallback in `isMVCGenInvariantType` that was
needed for bootstrapping (cf. #12874). It also extracts
`StringInvariant` and `StringSliceInvariant` as named abbreviations
tagged with `@[mvcgen_invariant_type]`, so that `mvcgen` classifies
string and string slice loop invariants correctly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sebastian Graf
2026-03-11 18:00:24 +08:00
committed by GitHub
parent 4450ff8995
commit 17807e1cbe
2 changed files with 30 additions and 6 deletions

View File

@@ -262,10 +262,9 @@ builtin_initialize mvcgenInvariantAttr : TagAttribute ←
/--
Returns `true` if `ty` is an application of a type tagged with `@[mvcgen_invariant_type]`.
Falls back to checking for `Std.Do.Invariant` for bootstrapping purposes.
-/
def isMVCGenInvariantType (env : Environment) (ty : Expr) : Bool :=
if let .const name .. := ty.getAppFn then
mvcgenInvariantAttr.hasTag env name || name == ``Std.Do.Invariant
mvcgenInvariantAttr.hasTag env name
else
false

View File

@@ -688,6 +688,7 @@ After leaving the loop, the cursor's prefix is `xs` and the suffix is empty.
During the induction step, the invariant holds for a suffix with head element `x`.
After running the loop body, the invariant then holds after shifting `x` to the prefix.
-/
@[mvcgen_invariant_type]
abbrev Invariant {α : Type u₁} (xs : List α) (β : Type u₂) (ps : PostShape.{max u₁ u₂}) :=
PostCond (List.Cursor xs × β) ps
@@ -1997,6 +1998,18 @@ theorem Spec.foldlM_array {α β : Type u} {m : Type u → Type v} {ps : PostSha
simp
apply Spec.foldlM_list inv step
/--
The type of loop invariants used by the specifications of `for ... in ...` loops over strings.
A loop invariant is a `PostCond` that takes as parameters
* A `String.Pos` representing the current position in the string `s`.
* A state tuple of type `β`, which will be a nesting of `MProd`s representing the elaboration of
`let mut` variables and early return.
-/
@[mvcgen_invariant_type]
abbrev StringInvariant (s : String) (β : Type u) (ps : PostShape.{u}) :=
PostCond (s.Pos × β) ps
/--
Helper definition for specifying loop invariants for loops with early return.
@@ -2019,7 +2032,7 @@ abbrev StringInvariant.withEarlyReturn {s : String}
(onContinue : s.Pos β Assertion ps)
(onReturn : γ β Assertion ps)
(onExcept : ExceptConds ps := ExceptConds.false) :
PostCond (s.Pos × MProd (Option γ) β) ps
StringInvariant s (MProd (Option γ) β) ps
:=
fun pos, x, b => spred(
(x = none onContinue pos b)
@@ -2029,7 +2042,7 @@ abbrev StringInvariant.withEarlyReturn {s : String}
@[spec]
theorem Spec.forIn_string
{s : String} {init : β} {f : Char β m (ForInStep β)}
(inv : PostCond (s.Pos × β) ps)
(inv : StringInvariant s β ps)
(step : pos b (h : pos s.endPos),
Triple
(f (pos.get h) b)
@@ -2057,6 +2070,18 @@ theorem Spec.forIn_string
next b => simp [ih _ _ hsp.next]
| endPos => simpa using Triple.pure _ (by simp)
/--
The type of loop invariants used by the specifications of `for ... in ...` loops over string slices.
A loop invariant is a `PostCond` that takes as parameters
* A `String.Slice.Pos` representing the current position in the string slice `s`.
* A state tuple of type `β`, which will be a nesting of `MProd`s representing the elaboration of
`let mut` variables and early return.
-/
@[mvcgen_invariant_type]
abbrev StringSliceInvariant (s : String.Slice) (β : Type u) (ps : PostShape.{u}) :=
PostCond (s.Pos × β) ps
/--
Helper definition for specifying loop invariants for loops with early return.
@@ -2079,7 +2104,7 @@ abbrev StringSliceInvariant.withEarlyReturn {s : String.Slice}
(onContinue : s.Pos β Assertion ps)
(onReturn : γ β Assertion ps)
(onExcept : ExceptConds ps := ExceptConds.false) :
PostCond (s.Pos × MProd (Option γ) β) ps
StringSliceInvariant s (MProd (Option γ) β) ps
:=
fun pos, x, b => spred(
(x = none onContinue pos b)
@@ -2089,7 +2114,7 @@ abbrev StringSliceInvariant.withEarlyReturn {s : String.Slice}
@[spec]
theorem Spec.forIn_stringSlice
{s : String.Slice} {init : β} {f : Char β m (ForInStep β)}
(inv : PostCond (s.Pos × β) ps)
(inv : StringSliceInvariant s β ps)
(step : pos b (h : pos s.endPos),
Triple
(f (pos.get h) b)